Linux cgroups development
 help / color / mirror / Atom feed
* Re: [PATCH 4/4] memcg: multi objcg charge support
From: Harry Yoo @ 2026-05-20  9:35 UTC (permalink / raw)
  To: Shakeel Butt, Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Meta kernel team,
	linux-mm, cgroups, linux-kernel, kernel test robot
In-Reply-To: <20260520053123.2709959-5-shakeel.butt@linux.dev>



On 5/20/26 2:31 PM, Shakeel Butt wrote:
> Commit 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg
> per-node type") split a memcg's single obj_cgroup into one per NUMA
> node so that reparenting LRU folios can take per-node lru locks. As a
> side effect, the per-CPU obj_stock_pcp -- which caches exactly one
> cached_objcg -- thrashes on workloads where threads of the same memcg
> run on different NUMA nodes. The kernel test robot reported a 67.7%
> regression on stress-ng.switch.ops_per_sec from this pattern.
> 
> Mirror the multi-slot pattern already used by memcg_stock_pcp: turn
> nr_bytes and cached_objcg into NR_OBJ_STOCK-element arrays, scan all
> slots on consume/refill/account, prefer empty slots when inserting,
> and evict a random slot only when full. With multiple slots a CPU can
> hold the per-node objcg variants of one memcg plus a few siblings
> without ever forcing a drain.
> 
> A single int8_t index records which slot the cached slab stats belong
> to; the stats are flushed on slot or pgdat change. With NR_OBJ_STOCK
> = 5 the layout (verified with pahole) is:
> 
>    offset 0  : lock(1) + index(1) + node_id(2) + slab stats(4) = 8B
>    offset 8  : nr_bytes[5]                                     = 10B
>    offset 18 : padding                                         = 6B
>    offset 24 : cached[5]                                       = 40B
>    offset 64 : (line 2) work_struct + flags (cold)
> 
> so consume_obj_stock, refill_obj_stock and the slab account path each
> touch exactly one 64-byte cache line on non-debug 64-bit builds.
> 
> Reported-by: kernel test robot <oliver.sang@intel.com>
> Closes: https://lore.kernel.org/oe-lkp/202605121641.b6a60cb0-lkp@intel.com
> Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type")
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Tested-by: kernel test robot <oliver.sang@intel.com>
> ---
> @@ -3350,19 +3405,45 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
>   		goto out;
>   	}
>   
> -	stock_nr_bytes = stock->nr_bytes;
> -	if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */
> -		drain_obj_stock(stock);
> +	for (i = 0; i < NR_OBJ_STOCK; ++i) {
> +		struct obj_cgroup *cached = READ_ONCE(stock->cached[i]);
> +
> +		if (!cached) {
> +			if (empty_slot == -1)
> +				empty_slot = i;
> +			continue;
> +		}
> +		if (cached == objcg) {
> +			slot = i;
> +			break;
> +		}
> +	}
> +
> +	if (slot == -1) {
> +		slot = empty_slot;
> +		if (slot == -1) {
> +			slot = get_random_u32_below(NR_OBJ_STOCK);

It would break kmalloc_nolock() because _get_random_bytes() uses a 
spinlock. perhaps prandom_u32_state() should be sufficient in this case.

Is there a reason why it uses random eviction, unlike multi-memcg percpu 
charge cache?

Otherwise LGTM!

-- 
Cheers,
Harry / Hyeonggon


^ permalink raw reply

* [PATCH] selftests/cgroup: enable memory controller in hugetlb memcg test
From: Guopeng Zhang @ 2026-05-20  9:31 UTC (permalink / raw)
  To: Shuah Khan, Tejun Heo, Michal Koutný
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Shakeel Butt,
	Muchun Song, Oscar Salvador, David Hildenbrand, cgroups, linux-mm,
	linux-kselftest, linux-kernel, Guopeng Zhang

test_hugetlb_memcg creates a child cgroup and then writes memory.max and
memory.swap.max. When the test is run standalone, the memory controller
may not be enabled in the test root cgroup's subtree_control.

In that case, the child cgroup is created without the memory control
files, and the test fails during setup before reaching the hugetlb memcg
accounting checks.

Skip the test when the memory controller is unavailable. Otherwise, enable
it in subtree_control before creating the test cgroup.

Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
---
Tested with a cgroup namespace where memory is available in
cgroup.controllers but not enabled in cgroup.subtree_control:

  before: test_hugetlb_memcg failed with "fail to set cgroup memory limit"
  after:  test_hugetlb_memcg passed and cgroup.subtree_control contained memory

 tools/testing/selftests/cgroup/test_hugetlb_memcg.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tools/testing/selftests/cgroup/test_hugetlb_memcg.c b/tools/testing/selftests/cgroup/test_hugetlb_memcg.c
index f451aa449be6..b627d84358b1 100644
--- a/tools/testing/selftests/cgroup/test_hugetlb_memcg.c
+++ b/tools/testing/selftests/cgroup/test_hugetlb_memcg.c
@@ -217,6 +217,14 @@ int main(int argc, char **argv)
 	if (cg_find_unified_root(root, sizeof(root), NULL))
 		ksft_exit_skip("cgroup v2 isn't mounted\n");
 
+	if (cg_read_strstr(root, "cgroup.controllers", "memory"))
+		ksft_exit_skip("memory controller isn't available\n");
+
+	if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) {
+		if (cg_write(root, "cgroup.subtree_control", "+memory"))
+			ksft_exit_skip("Failed to set memory controller\n");
+	}
+
 	switch (test_hugetlb_memcg(root)) {
 	case KSFT_PASS:
 		ksft_test_result_pass("test_hugetlb_memcg\n");
-- 
2.43.0

^ permalink raw reply related

* Re: [PATCH 0/8] per-memcg-per-node kmem accounting
From: Alexandre Ghiti @ 2026-05-20  8:39 UTC (permalink / raw)
  To: Joshua Hahn
  Cc: Andrew Morton, Michal Hocko, Roman Gushchin, Shakeel Butt,
	Muchun Song, Dennis Zhou, Tejun Heo, Christoph Lameter,
	Vlastimil Babka, Yosry Ahmed, Nhat Pham, Sergey Senozhatsky,
	Chengming Zhou, Suren Baghdasaryan, Qi Zheng, David Hildenbrand,
	Lorenzo Stoakes, Minchan Kim, Mike Rapoport, Axel Rasmussen,
	Barry Song, Kairui Song, Wei Xu, Yuanchu Xie, Liam R . Howlett,
	linux-mm, linux-kernel, cgroups
In-Reply-To: <20260518145732.349196-1-joshua.hahnjy@gmail.com>

Hi Joshua,

On 5/18/26 16:57, Joshua Hahn wrote:
> On Mon, 11 May 2026 22:20:35 +0200 Alexandre Ghiti <alex@ghiti.fr> wrote:
>
>> This series pursues the work initiated by Joshua [1]. We need kernel
>> memory to be accounted on a per-node basis in order to be able to
>> know the memcg and physical memory association.
>>    
>> This series takes advantage of the recent introduction of per-node
>> obj_cgroup [2] and makes those obj_cgroup tied to their numa node.
>>    
>> The bulk of the series is percpu per-node accounting: percpu
>> "precharges" the memcg before we know the actual location of the pages
>> it uses, so charging and accounting had to be split. All other kmem
>> users (slab, zswap, __memcg_kmem_charge_page) are straightforward
>> conversions (zswap support is limited in this series because Joshua
>> is working on it in parallel [3]).
>>   
>> Thanks Joshua for your early feedbacks!
> Hello Alex,
>
> Thank you for your work!
>
> Overall I think the direction makes sense to me. Pre-overcharging makes sense to
> me as an approach, we would much rather overaccount than underaccount and
> later have to breach limits.
>
> I do have some concerns on performance, though. Namely, I think there are
> some expensive operations that I think would benefit from some performane
> benchmarking with this patch added (maybe some simple microbenchmarks that
> demonstrates kernel allocation overhead could be useful).
>
>  From what I can tell, there is some additional performance overhead that has
> to do with iterating over num_possible_cpus() x pages_per_alloc, which
> doesn't seem trivial to me.


Indeed, let me microbenchmark the overhead on a large system.


>
> Another concern that I see is the stock credit system. Maybe we could be
> bypassing the stock check leading to more time spent doing the atomic
> operations.


I'm not following on this one, which atomic operations do you see that 
could be bypassed?


>
> obj_stock caches a single obj_cgroup, which means that if we split the objcg
> to be per-node (in patch 6), then the obj_stock basically gets invalidated
> every operation since we iterate over more objcgs (even though we are in
> the same logical objcg). Maybe I'm missing something?


The objcg split comes from commit 01b9da291c49 ("mm: memcontrol: convert 
objcg to be per-memcg per-node type") and the problem you describe is 
exactly what Shakeel is trying to fix [1].

But I remember trying a microbenchmark and noticed a +5% regression (on 
top of the 67% then...), I'll rebase this series on top of Shakeel's and 
re-run.

[1] 
https://lore.kernel.org/linux-mm/20260520053123.2709959-1-shakeel.butt@linux.dev/T/#m127d4969b105c046a2a21e3c79c963771007583d


>
> I haven't taken a deep look at the implementation details but just wanted to
> raise some high level items that I noticed. Of course, all of these concerns
> are just theoretical, if you can show that the performance delta is not
> noticable then all of my concerns don't matter.
>
> I also want to talk more about the local credit system but let's first see
> what the numbers are first.
>
> Thanks again, Alex. And I really like patch 2 because it is a solution to
> a problem that I ran into in my percpu tracking series that I couldn't think
> of before! Thank you for solving my problem too : -)


Great then, thanks :)

Alex


>
> Have a great day!
> Joshua
>     
>> [1] https://lore.kernel.org/linux-mm/20260404033844.1892595-1-joshua.hahnjy@gmail.com/
>> [2] https://lore.kernel.org/linux-mm/56c04b1c5d54f75ccdc12896df6c1ca35403ecc3.1772711148.git.zhengqi.arch@bytedance.com/
>> [3] https://lore.kernel.org/linux-mm/20260311195153.4013476-1-joshua.hahnjy@gmail.com/
>>
>> Alexandre Ghiti (8):
>>    mm: memcontrol: propagate NMI slab stats to memcg vmstats
>>    mm: percpu: charge obj_exts allocation with __GFP_ACCOUNT
>>    mm: percpu: Split memcg charging and kmem accounting
>>    mm: memcontrol: track MEMCG_KMEM per NUMA node
>>    mm: memcontrol: per-node kmem accounting for page charges
>>    mm: slab: per-node kmem accounting for slab
>>    mm: percpu: per-node kmem accounting using local credit
>>    mm: zswap: per-node kmem accounting for zswap/zsmalloc
>>
>>   include/linux/memcontrol.h |  27 +++++--
>>   include/linux/mmzone.h     |   1 +
>>   include/linux/zsmalloc.h   |   2 +
>>   mm/memcontrol.c            | 150 ++++++++++++++++++++++++++++---------
>>   mm/percpu-internal.h       |  16 +---
>>   mm/percpu.c                |  90 ++++++++++++++++++++--
>>   mm/vmstat.c                |   1 +
>>   mm/zsmalloc.c              |  11 +++
>>   mm/zswap.c                 |   9 ++-
>>   9 files changed, 242 insertions(+), 65 deletions(-)
>>
>> -- 
>> 2.54.0
>>
>>

^ permalink raw reply

* Re: [PATCH cgroup/for-next v2 0/5] cgroup/cpuset: Support multiple source/destination cpusets for cpuset_*attach()
From: Ridong Chen @ 2026-05-20  8:29 UTC (permalink / raw)
  To: Waiman Long, Chen Ridong, Tejun Heo, Johannes Weiner,
	Michal Koutný, Ingo Molnar, Peter Zijlstra, Juri Lelli,
	Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak
  Cc: cgroups, linux-kernel, Aaron Tomlin
In-Reply-To: <20260516042448.698216-1-longman@redhat.com>



On 2026/5/16 12:24, Waiman Long wrote:
> Sashiko AI review of another cpuset patch had found that cpuset_attach()
> and cpuset_can_attach() can be passed a cgroup_taskset with tasks
> migrating from one source cpuset to multiple destination cpusets and
> vice versa.  Further testing of the cpuset code indicates that this is
> indeed the case when the v2 cpuset controller is enabled or disabled.
> 
> Unfortunately, cpuset_attach() and cpuset_can_attach() still assume that
> there will be one source and one destinaton cpuset which may result in
> inocrrect behavior.
> 

Hi Longman,

I am thinking whether we can use the pids subsystem's approach to solve 
this issue, which I think could be much simpler.

For the DL task accounting, we can handle it the same way 
pids_can_attach() does - just call task_cs(task) for each task 
individually inside the can_attach() loop and do the nr_deadline_tasks 
adjustment right there. This eliminates the need to pass per-task source 
cpuset information to the attach() callback entirely for DL accounting 
purposes.

For cpuset_migrate_mm(), I don't think we need per-task oldcs storage in 
task_struct either. The scenarios where multiple source cpusets are 
involved are:

enable cpuset controller: child cpusets inherit parent's effective_mems, 
so attach_mems_updated is false and cpuset_migrate_mm() is never called.

disable cpuset controller: tasks move from children to parent. Since 
children's effective_mems is always a subset of parent's effective_mems, 
even if cpuset_migrate_mm() is triggered, it's effectively a noop (no 
pages need to move from a subset to its superset).

cgroup.procs write with threads in different cpusets: this is a 
many-to-one migration with a single process, so there is only one 
group_leader and one mm. We only need to record the leader's oldcs, 
which a single static variable can handle.

So in all cases, the migration path only needs one oldcs for the leader. 
We don't need to add a field to task_struct.

What do you think?



> This patch series is created to fix this issue. The first 2 patches are
> just preparatory patches to make the remaining patches easier to review.
> 
> Patch 3 adds a new attach_old_cs field into task_struct to track the
> old cpuset to be used in case when cpuset_migrate_mm() needs to be
> called in cpuset_attach().
> 
> Patch 4 moves mpol_rebind_mm() and cpuset_migrate_mm() inside
> cpuset_attach_task() to make CLONE_INTO_CGROUP flag of clone(2) works
> more like moving task from one cpuset to another one, while also make
> supporting multiple source and destination cpusets easier.
> 
> Patch 5 makes the necessary changes to enable the support of multiple
> source and destination cpusets by keeping all the source and destination
> cpusets found during task iterations in two singly linked lists for
> source and destination cpusets respectively.
> 
> Waiman Long (5):
>    cgroup/cpuset: Add a cpuset_reserve_dl_bw() helper
>    cgroup/cpuset: Expand the scope of cpuset_can_attach_check()
>    cgroup/cpuset: Replace cpuset_attach_old_cs by a new attach_old_cs
>      field in task_struct
>    cgroup/cpuset: Move mpol_rebind_mm/cpuset_migrate_mm() calls inside
>      cpuset_attach_task()
>    cgroup/cpuset: Support multiple source/destination cpusets for
>      cpuset_*attach()
> 
>   include/linux/sched.h           |   3 +
>   kernel/cgroup/cpuset-internal.h |   6 +
>   kernel/cgroup/cpuset.c          | 358 +++++++++++++++++++++-----------
>   3 files changed, 249 insertions(+), 118 deletions(-)
> 

-- 
Best regards,
Ridong

^ permalink raw reply

* Re: [PATCH 3/4] memcg: int16_t for cached slab stats
From: Harry Yoo @ 2026-05-20  7:25 UTC (permalink / raw)
  To: Shakeel Butt, Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Meta kernel team,
	linux-mm, cgroups, linux-kernel, kernel test robot
In-Reply-To: <20260520053123.2709959-4-shakeel.butt@linux.dev>



On 5/20/26 2:31 PM, Shakeel Butt wrote:
> Currently struct obj_stock_pcp stores cached slab stats in 'int' which
> is 4 bytes per counter on 64-bit machines. Switch them to int16_t to
> shrink the cached metadata.
> 
> The existing PAGE_SIZE flush in __account_obj_stock() bounds *bytes at
> PAGE_SIZE on 4KiB and 16KiB page archs, well within int16_t. On 64KiB
> pages PAGE_SIZE is well above S16_MAX so that flush never fires, and a
> sufficiently long run of accumulations would overflow the cache. Add
> an explicit S16_MAX guard before each add: when the next add would
> push abs(*bytes) past S16_MAX, fold the cached value into @nr and
> flush directly via mod_objcg_mlstate() before the accumulation.
> 
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Tested-by: kernel test robot <oliver.sang@intel.com>
> ---

So arches with 64KiB sizes won't benefit from

"Even for large object >= PAGE_SIZE, the vmstat data will still be 
cached locally at least once before pushing it out" case.

But its benefit is questionable (to me), might be ok.

>   mm/memcontrol.c | 16 +++++++++++++---
>   1 file changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index b3d63d9f267c..1ed27fd06850 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -3158,7 +3158,7 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
>   				struct obj_stock_pcp *stock, int nr,
>   				struct pglist_data *pgdat, enum node_stat_item idx)
>   {
> -	int *bytes;
> +	int16_t *bytes;
>   
>   	/*
>   	 * Though at the moment MAX_NUMNODES <= 1024 in all archs but let's make
> @@ -3195,6 +3195,16 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
>   
>   	bytes = (idx == NR_SLAB_RECLAIMABLE_B) ? &stock->nr_slab_reclaimable_b
>   					       : &stock->nr_slab_unreclaimable_b;
> +	/*
> +	 * To avoid overflow or underflow, flush directly if accumulating @nr
> +	 * would push the cached value past S16_MAX.
> +	 */
> +	if (abs(nr + *bytes) >= S16_MAX) {
nit: should be > S16_MAX?

> +		nr += *bytes;
> +		*bytes = 0;
> +		goto direct;
> +	}
> +
>   	/*
>   	 * Even for large object >= PAGE_SIZE, the vmstat data will still be
>   	 * cached locally at least once before pushing it out.

FWIW:
Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org>

-- 
Cheers,
Harry / Hyeonggon


^ permalink raw reply

* Re: [PATCH v2 1/2] mm/memcontrol: add dmem charge/uncharge functions
From: Albert Esteve @ 2026-05-20  7:22 UTC (permalink / raw)
  To: Eric Chanudet
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Shakeel Butt,
	Muchun Song, Andrew Morton, Maarten Lankhorst, Maxime Ripard,
	Natalie Vock, Tejun Heo, Michal Koutný, Jonathan Corbet,
	Shuah Khan, cgroups, linux-mm, linux-kernel, dri-devel,
	T.J. Mercier, Christian König, Maxime Ripard, Dave Airlie,
	linux-doc
In-Reply-To: <20260519-cgroup-dmem-memcg-double-charge-v2-1-db4d1407062b@redhat.com>

On Tue, May 19, 2026 at 6:01 PM Eric Chanudet <echanude@redhat.com> wrote:
>
> Add mem_cgroup_dmem_charge() and mem_cgroup_dmem_uncharge() to allow
> dmem pool allocations to optionally be double-charged against the memory
> controller. Take the struct cgroup from the dmem pool's css as there is
> no convenient object exported to represent these allocations. These will
> resolve the effective memory css from that cgroup and perform the
> charge.
>
> Introduce a MEMCG_DMEM stat counter to memory.stat to make the cgroup's
> dmem charge visible.
>
> Signed-off-by: Eric Chanudet <echanude@redhat.com>

Reviewed-by: Albert Esteve <aesteve@redhat.com>

> ---
>  include/linux/memcontrol.h | 16 ++++++++++++
>  mm/memcontrol.c            | 65 ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 81 insertions(+)
>
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index dc3fa687759b45748b2acee6d7f43da325eb50c1..8e1d49b87fb64e6114f3eb920293e14920290fe7 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -39,6 +39,7 @@ enum memcg_stat_item {
>         MEMCG_ZSWAP_B,
>         MEMCG_ZSWAPPED,
>         MEMCG_ZSWAP_INCOMP,
> +       MEMCG_DMEM,
>         MEMCG_NR_STAT,
>  };
>
> @@ -1872,6 +1873,21 @@ static inline bool mem_cgroup_zswap_writeback_enabled(struct mem_cgroup *memcg)
>  }
>  #endif
>
> +#if defined(CONFIG_MEMCG) && defined(CONFIG_CGROUP_DMEM)
> +bool mem_cgroup_dmem_charge(struct cgroup *cgrp, unsigned int nr_pages,
> +                           gfp_t gfp_mask);
> +void mem_cgroup_dmem_uncharge(struct cgroup *cgrp, unsigned int nr_pages);
> +#else
> +static inline bool mem_cgroup_dmem_charge(struct cgroup *cgrp,
> +                                         unsigned int nr_pages, gfp_t gfp_mask)
> +{
> +       return true;
> +}
> +static inline void mem_cgroup_dmem_uncharge(struct cgroup *cgrp,
> +                                           unsigned int nr_pages)
> +{
> +}
> +#endif
>
>  /* Cgroup v1-related declarations */
>
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index c03d4787d466803db49cdaa90e6d6ba426b7afe2..91a7ac16b6eac2d6c3700b6885a068bf8b640706 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -433,6 +433,7 @@ static const unsigned int memcg_stat_items[] = {
>         MEMCG_ZSWAP_B,
>         MEMCG_ZSWAPPED,
>         MEMCG_ZSWAP_INCOMP,
> +       MEMCG_DMEM,
>  };
>
>  #define NR_MEMCG_NODE_STAT_ITEMS ARRAY_SIZE(memcg_node_stat_items)
> @@ -1606,6 +1607,9 @@ static const struct memory_stat memory_stats[] = {
>  #ifdef CONFIG_NUMA_BALANCING
>         { "pgpromote_success",          PGPROMOTE_SUCCESS       },
>  #endif
> +#ifdef CONFIG_CGROUP_DMEM
> +       { "dmem",                       MEMCG_DMEM              },
> +#endif
>  };
>
>  /* The actual unit of the state item, not the same as the output unit */
> @@ -5909,6 +5913,67 @@ static struct cftype zswap_files[] = {
>  };
>  #endif /* CONFIG_ZSWAP */
>
> +#ifdef CONFIG_CGROUP_DMEM
> +/**
> + * mem_cgroup_dmem_charge - charge memcg for a dmem pool allocation
> + * @cgrp: cgroup of the dmem pool
> + * @nr_pages: number of pages to charge
> + * @gfp_mask: reclaim mode
> + *
> + * Charges @nr_pages to @memcg. Returns %true if the charge fit within
> + * @memcg's configured limit, %false if it doesn't.
> + */
> +bool mem_cgroup_dmem_charge(struct cgroup *cgrp, unsigned int nr_pages,
> +                           gfp_t gfp_mask)
> +{
> +       struct cgroup_subsys_state *mem_css;
> +       struct mem_cgroup *memcg;
> +
> +       /* CGROUP_DMEM and MEMCG guarantees this cannot be NULL. */
> +       mem_css = cgroup_get_e_css(cgrp, &memory_cgrp_subsys);
> +
> +       /* Use the memcg, if any, of the dmem cgroup. */
> +       memcg = mem_cgroup_from_css(mem_css);
> +       if (!memcg || mem_cgroup_is_root(memcg)) {
> +               css_put(mem_css);
> +               return false;
> +       }
> +
> +       if (try_charge_memcg(memcg, gfp_mask, nr_pages)) {
> +               css_put(mem_css);
> +               return false;
> +       }
> +
> +       mod_memcg_state(memcg, MEMCG_DMEM, nr_pages);
> +       css_put(mem_css);
> +       return true;
> +}
> +
> +/**
> + * mem_cgroup_dmem_uncharge - uncharge memcg from a dmem pool allocation
> + * @cgrp: cgroup of the dmem pool
> + * @nr_pages: number of pages to uncharge
> + */
> +void mem_cgroup_dmem_uncharge(struct cgroup *cgrp, unsigned int nr_pages)
> +{
> +       struct cgroup_subsys_state *mem_css;
> +       struct mem_cgroup *memcg;
> +
> +       /* CGROUP_DMEM and MEMCG guarantees this cannot be NULL. */
> +       mem_css = cgroup_get_e_css(cgrp, &memory_cgrp_subsys);
> +
> +       memcg = mem_cgroup_from_css(mem_css);
> +       if (!memcg || mem_cgroup_is_root(memcg)) {
> +               css_put(mem_css);
> +               return;
> +       }
> +
> +       mod_memcg_state(memcg, MEMCG_DMEM, -nr_pages);
> +       refill_stock(memcg, nr_pages);
> +       css_put(mem_css);
> +}
> +#endif /* CONFIG_CGROUP_DMEM */
> +
>  static int __init mem_cgroup_swap_init(void)
>  {
>         if (mem_cgroup_disabled())
>
> --
> 2.52.0
>


^ permalink raw reply

* Re: [PATCH 2/4] memcg: uint16_t for nr_bytes in obj_stock_pcp
From: Harry Yoo @ 2026-05-20  7:01 UTC (permalink / raw)
  To: Shakeel Butt, Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Meta kernel team,
	linux-mm, cgroups, linux-kernel, kernel test robot
In-Reply-To: <20260520053123.2709959-3-shakeel.butt@linux.dev>



On 5/20/26 2:31 PM, Shakeel Butt wrote:
> Currently struct obj_stock_pcp stores nr_bytes in an 'unsigned int'
> which is 4 bytes on 64-bit machines. Switch the field to uint16_t to
> shrink the per-CPU cache.
> 
> The kernel supports PAGE_SIZE_4KB, _8KB, _16KB, _32KB, _64KB and
> _256KB (see HAVE_PAGE_SIZE_* in arch/Kconfig). After the
> PAGE_SIZE-aligned flush in __refill_obj_stock(), the sub-page
> remainder fits in uint16_t up through 64KiB pages where PAGE_SIZE - 1
> == U16_MAX, but on 256KiB pages PAGE_SIZE - 1 == 0x3FFFF exceeds
> U16_MAX. The accumulator also needs to stay within uint16_t between
> page-aligned flushes on 64KiB pages where PAGE_SIZE itself is
> U16_MAX + 1.
> 
> Accumulate the new total in an 'unsigned int' local, then:
> 
>    1. Flush whenever the accumulator would hit U16_MAX. Together with
>       the existing allow_uncharge flush at PAGE_SIZE, this keeps the
>       uint16_t safe on PAGE_SIZE <= 64KiB.
> 
>    2. On configs with PAGE_SHIFT > 16 (PAGE_SIZE_256KB on hexagon and
>       powerpc 44x), push any sub-page remainder above U16_MAX into
>       objcg->nr_charged_bytes via atomic_add before storing back, so
>       the store cannot silently truncate. The PAGE_SHIFT > 16 guard
>       folds the branch out at compile time on smaller page sizes.
> 
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Tested-by: kernel test robot <oliver.sang@intel.com>
> ---
>   mm/memcontrol.c | 33 +++++++++++++++++++++++++++------
>   1 file changed, 27 insertions(+), 6 deletions(-)
> 
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index d7c162946719..b3d63d9f267c 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -3339,21 +3340,41 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
>   		goto out;
>   	}
>   
> +	stock_nr_bytes = stock->nr_bytes;
>   	if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */
>   		drain_obj_stock(stock);
>   		obj_cgroup_get(objcg);
> -		stock->nr_bytes = atomic_read(&objcg->nr_charged_bytes)
> +		stock_nr_bytes = atomic_read(&objcg->nr_charged_bytes)
>   				? atomic_xchg(&objcg->nr_charged_bytes, 0) : 0;
>   		WRITE_ONCE(stock->cached_objcg, objcg);
>   
>   		allow_uncharge = true;	/* Allow uncharge when objcg changes */
>   	}
> -	stock->nr_bytes += nr_bytes;
> +	stock_nr_bytes += nr_bytes;
> +
> +	/* Since stock->nr_bytes is uint16_t, don't refill >= U16_MAX */
> +	if ((allow_uncharge && (stock_nr_bytes > PAGE_SIZE)) ||
> +	    stock_nr_bytes >= U16_MAX) {

nit: This should be > U16_MAX?

> +		nr_pages = stock_nr_bytes >> PAGE_SHIFT;
> +		stock_nr_bytes &= (PAGE_SIZE - 1);
> +
> +		/*
> +		 * On configs with PAGE_SHIFT > 16 (PAGE_SIZE_256KB on
> +		 * hexagon and powerpc 44x), the sub-page remainder can
> +		 * still exceed U16_MAX. Push the excess back to
> +		 * objcg->nr_charged_bytes so the store into uint16_t
> +		 * cannot silently truncate; folded out at compile time
> +		 * on smaller page sizes.
> +		 */
> +		if (PAGE_SHIFT > 16 && stock_nr_bytes > U16_MAX) {
> +			unsigned int kept = stock_nr_bytes & U16_MAX;
>   
> -	if (allow_uncharge && (stock->nr_bytes > PAGE_SIZE)) {
> -		nr_pages = stock->nr_bytes >> PAGE_SHIFT;
> -		stock->nr_bytes &= (PAGE_SIZE - 1);
> +			atomic_add(stock_nr_bytes - kept,
> +				   &objcg->nr_charged_bytes);
> +			stock_nr_bytes = kept;
> +		}
>   	}
> +	stock->nr_bytes = stock_nr_bytes;
>   
>   out:
>   	if (nr_pages)

-- 
Cheers,
Harry / Hyeonggon


^ permalink raw reply

* Re: [PATCH 2/4] memcg: uint16_t for nr_bytes in obj_stock_pcp
From: Harry Yoo @ 2026-05-20  6:41 UTC (permalink / raw)
  To: Shakeel Butt, Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Meta kernel team,
	linux-mm, cgroups, linux-kernel, kernel test robot
In-Reply-To: <20260520053123.2709959-3-shakeel.butt@linux.dev>



On 5/20/26 2:31 PM, Shakeel Butt wrote:
> Currently struct obj_stock_pcp stores nr_bytes in an 'unsigned int'
> which is 4 bytes on 64-bit machines. Switch the field to uint16_t to
> shrink the per-CPU cache.
> 
> The kernel supports PAGE_SIZE_4KB, _8KB, _16KB, _32KB, _64KB and
> _256KB (see HAVE_PAGE_SIZE_* in arch/Kconfig). After the
> PAGE_SIZE-aligned flush in __refill_obj_stock(), the sub-page
> remainder fits in uint16_t up through 64KiB pages where PAGE_SIZE - 1
> == U16_MAX, but on 256KiB pages PAGE_SIZE - 1 == 0x3FFFF exceeds
> U16_MAX. The accumulator also needs to stay within uint16_t between
> page-aligned flushes on 64KiB pages where PAGE_SIZE itself is
> U16_MAX + 1.
> 
> Accumulate the new total in an 'unsigned int' local, then:
> 
>    1. Flush whenever the accumulator would hit U16_MAX. Together with
>       the existing allow_uncharge flush at PAGE_SIZE, this keeps the
>       uint16_t safe on PAGE_SIZE <= 64KiB.
> 
>    2. On configs with PAGE_SHIFT > 16 (PAGE_SIZE_256KB on hexagon and
>       powerpc 44x), push any sub-page remainder above U16_MAX into
>       objcg->nr_charged_bytes via atomic_add before storing back, so
>       the store cannot silently truncate. The PAGE_SHIFT > 16 guard
>       folds the branch out at compile time on smaller page sizes.
> 
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Tested-by: kernel test robot <oliver.sang@intel.com>
> ---

Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org>

-- 
Cheers,
Harry / Hyeonggon


^ permalink raw reply

* [PATCH] blk-throttle: schedule parent dispatch in tg_flush_bios()
From: Tao Cui @ 2026-05-20  6:24 UTC (permalink / raw)
  To: tj, josef, axboe, cgroups; +Cc: linux-block, Tao Cui

tg_flush_bios() schedules pending_timer on the child tg's own
service_queue, which causes throtl_pending_timer_fn() to dispatch from
the child's pending_tree.  For leaf cgroups this tree is empty, so the
timer fires and exits without dispatching the throttled bio.

The throttled bio sits in the parent's pending_tree with disptime set
to jiffies (THROTL_TG_CANCELING zeroes all dispatch times), but the
parent's timer is never explicitly rescheduled.  The bio only gets
dispatched when the parent timer eventually fires at its previously
scheduled expiry.

Fix by calling throtl_schedule_next_dispatch(sq->parent_sq, true)
instead, matching what tg_set_limit() already does.  This forces the
parent's dispatch cycle to run immediately and flush all canceling
bios without waiting for a stale timer.

Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
 block/blk-throttle.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 2d39977ba9de..0ad30b688ce1 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -1652,7 +1652,7 @@ static void tg_flush_bios(struct throtl_grp *tg)
 	 */
 	tg_update_disptime(tg);
 
-	throtl_schedule_pending_timer(sq, jiffies + 1);
+	throtl_schedule_next_dispatch(sq->parent_sq, true);
 }
 
 static void throtl_pd_offline(struct blkg_policy_data *pd)
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH 1/4] memcg: store node_id instead of pglist_data pointer
From: Muchun Song @ 2026-05-20  6:13 UTC (permalink / raw)
  To: Shakeel Butt
  Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Harry Yoo,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <20260520053123.2709959-2-shakeel.butt@linux.dev>



> On May 20, 2026, at 13:31, Shakeel Butt <shakeel.butt@linux.dev> wrote:
> 
> The struct obj_stock_pcp stores a pointer to pglist_data for the slab
> stats cached on the cpu. On 64-bit machines, this costs 8 bytes. The
> pointer is not strictly required: NODE_DATA() can recover it from the
> node id. Replace cached_pgdat with int16_t node_id and use NUMA_NO_NODE
> as the "no stats cached" sentinel.
> 
> At the moment all the archs limit MAX_NUMNODES to 1024 so int16_t is
> plenty; a BUILD_BUG_ON() makes sure we notice if that ever changes.
> 
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Tested-by: kernel test robot <oliver.sang@intel.com>

Acked-by: Muchun Song <muchun.song@linux.dev>

Thanks.


^ permalink raw reply

* [PATCH] cgroup/dmem: implement dmem.high soft limit and throttling
From: Qiliang Yuan @ 2026-05-20  6:07 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Natalie Vock, Tejun Heo,
	Johannes Weiner, Michal Koutný
  Cc: cgroups, dri-devel, linux-kernel, Qiliang Yuan

Introduce the "high" soft limit for the dmem cgroup v2 controller.
When a cgroup's device memory usage exceeds its high limit, tasks
belonging to that cgroup are throttled by being forced into a sleep
before returning to user space, instead of being failed outright
as with the "max" limit.

Key changes:
- Add high counter configuration to dmem_cgroup_pool.
- Add over-high check in the try_charge path and set TIF_NOTIFY_RESUME.
- Inject the dmem throttling handler into resume_user_mode_work.
- Implement the handler to perform a 100ms interruptible sleep for
  over-limit tasks.

This mechanism provides smoother over-subscription support for device
memory resources.

Signed-off-by: Qiliang Yuan <realwujing@gmail.com>
---
This series introduces the "high" soft limit and associated task
throttling mechanism to the dmem cgroup v2 controller.

The device memory (VRAM) management currently only supports hard limits
(max), which leads to immediate allocation failures when reached. This
can be disruptive for GPU-bound AI workloads. By introducing a soft
limit, we allow cgroups to exceed their quota temporarily while
applying backpressure via task throttling before the process returns
to user space.

The mechanism is inspired by the memory cgroup's high limit:
- When usage > high, the task is marked with TIF_NOTIFY_RESUME.
- Upon returning to user space, it triggers a 100ms sleep.
- This provides a smoother over-subscription model for GPU resources.

Qiliang Yuan (1):

cgroup/dmem: implement dmem.high soft limit and throttling
---
To: Maarten Lankhorst <dev@lankhorst.se>
To: Maxime Ripard <mripard@kernel.org>
To: Natalie Vock <natalie.vock@gmx.de>
To: Tejun Heo <tj@kernel.org>
To: Johannes Weiner <hannes@cmpxchg.org>
To: Michal Koutný <mkoutny@suse.com>
Cc: cgroups@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org
---
 include/linux/cgroup_dmem.h      | 10 +++++++
 include/linux/resume_user_mode.h |  2 ++
 kernel/cgroup/dmem.c             | 60 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/include/linux/cgroup_dmem.h b/include/linux/cgroup_dmem.h
index dd4869f1d736e..d58972de7c910 100644
--- a/include/linux/cgroup_dmem.h
+++ b/include/linux/cgroup_dmem.h
@@ -21,6 +21,13 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size,
 			   struct dmem_cgroup_pool_state **ret_pool,
 			   struct dmem_cgroup_pool_state **ret_limit_pool);
 void dmem_cgroup_uncharge(struct dmem_cgroup_pool_state *pool, u64 size);
+void __dmem_cgroup_handle_over_high(void);
+
+static inline void dmem_cgroup_handle_over_high(void)
+{
+	__dmem_cgroup_handle_over_high();
+}
+
 bool dmem_cgroup_state_evict_valuable(struct dmem_cgroup_pool_state *limit_pool,
 				      struct dmem_cgroup_pool_state *test_pool,
 				      bool ignore_low, bool *ret_hit_low);
@@ -51,6 +58,9 @@ static inline int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64
 static inline void dmem_cgroup_uncharge(struct dmem_cgroup_pool_state *pool, u64 size)
 { }
 
+static inline void dmem_cgroup_handle_over_high(void)
+{ }
+
 static inline
 bool dmem_cgroup_state_evict_valuable(struct dmem_cgroup_pool_state *limit_pool,
 				      struct dmem_cgroup_pool_state *test_pool,
diff --git a/include/linux/resume_user_mode.h b/include/linux/resume_user_mode.h
index bf92227c78d0d..afcab20998c41 100644
--- a/include/linux/resume_user_mode.h
+++ b/include/linux/resume_user_mode.h
@@ -8,6 +8,7 @@
 #include <linux/memcontrol.h>
 #include <linux/rseq.h>
 #include <linux/blk-cgroup.h>
+#include <linux/cgroup_dmem.h>
 
 /**
  * set_notify_resume - cause resume_user_mode_work() to be called
@@ -58,6 +59,7 @@ static inline void resume_user_mode_work(struct pt_regs *regs)
 
 	mem_cgroup_handle_over_high(GFP_KERNEL);
 	blkcg_maybe_throttle_current();
+	dmem_cgroup_handle_over_high();
 
 	rseq_handle_slowpath(regs);
 }
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 4753a67d0f0f2..f77c692b887b1 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -15,6 +15,7 @@
 #include <linux/page_counter.h>
 #include <linux/parser.h>
 #include <linux/refcount.h>
+#include <linux/resume_user_mode.h>
 #include <linux/rculist.h>
 #include <linux/slab.h>
 
@@ -156,6 +157,12 @@ set_resource_low(struct dmem_cgroup_pool_state *pool, u64 val)
 	page_counter_set_low(&pool->cnt, val);
 }
 
+static void
+set_resource_high(struct dmem_cgroup_pool_state *pool, u64 val)
+{
+	page_counter_set_high(&pool->cnt, val);
+}
+
 static void
 set_resource_max(struct dmem_cgroup_pool_state *pool, u64 val)
 {
@@ -167,6 +174,11 @@ static u64 get_resource_low(struct dmem_cgroup_pool_state *pool)
 	return pool ? READ_ONCE(pool->cnt.low) : 0;
 }
 
+static u64 get_resource_high(struct dmem_cgroup_pool_state *pool)
+{
+	return pool ? READ_ONCE(pool->cnt.high) : 0;
+}
+
 static u64 get_resource_min(struct dmem_cgroup_pool_state *pool)
 {
 	return pool ? READ_ONCE(pool->cnt.min) : 0;
@@ -186,6 +198,7 @@ static void reset_all_resource_limits(struct dmem_cgroup_pool_state *rpool)
 {
 	set_resource_min(rpool, 0);
 	set_resource_low(rpool, 0);
+	set_resource_high(rpool, PAGE_COUNTER_MAX);
 	set_resource_max(rpool, PAGE_COUNTER_MAX);
 }
 
@@ -685,6 +698,9 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size,
 		goto err;
 	}
 
+	if (page_counter_read(&pool->cnt) > READ_ONCE(pool->cnt.high))
+		set_notify_resume(current);
+
 	/* On success, reference from get_current_dmemcs is transferred to *ret_pool */
 	*ret_pool = pool;
 	return 0;
@@ -835,13 +851,24 @@ static ssize_t dmem_cgroup_region_low_write(struct kernfs_open_file *of,
 	return dmemcg_limit_write(of, buf, nbytes, off, set_resource_low);
 }
 
+static int dmem_cgroup_region_high_show(struct seq_file *sf, void *v)
+{
+	return dmemcg_limit_show(sf, v, get_resource_high);
+}
+
+static ssize_t dmem_cgroup_region_high_write(struct kernfs_open_file *of,
+					  char *buf, size_t nbytes, loff_t off)
+{
+	return dmemcg_limit_write(of, buf, nbytes, off, set_resource_high);
+}
+
 static int dmem_cgroup_region_max_show(struct seq_file *sf, void *v)
 {
 	return dmemcg_limit_show(sf, v, get_resource_max);
 }
 
 static ssize_t dmem_cgroup_region_max_write(struct kernfs_open_file *of,
-				      char *buf, size_t nbytes, loff_t off)
+					  char *buf, size_t nbytes, loff_t off)
 {
 	return dmemcg_limit_write(of, buf, nbytes, off, set_resource_max);
 }
@@ -868,6 +895,12 @@ static struct cftype files[] = {
 		.seq_show = dmem_cgroup_region_low_show,
 		.flags = CFTYPE_NOT_ON_ROOT,
 	},
+	{
+		.name = "high",
+		.write = dmem_cgroup_region_high_write,
+		.seq_show = dmem_cgroup_region_high_show,
+		.flags = CFTYPE_NOT_ON_ROOT,
+	},
 	{
 		.name = "max",
 		.write = dmem_cgroup_region_max_write,
@@ -877,6 +910,31 @@ static struct cftype files[] = {
 	{ } /* Zero entry terminates. */
 };
 
+void __dmem_cgroup_handle_over_high(void)
+{
+	struct dmemcg_state *dmemcs;
+	struct dmem_cgroup_pool_state *pool;
+
+	dmemcs = css_to_dmemcs(task_get_css(current, dmem_cgrp_id));
+	if (!dmemcs)
+		return;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(pool, &dmemcs->pools, css_node) {
+		unsigned long usage, high;
+
+		usage = page_counter_read(&pool->cnt);
+		high = READ_ONCE(pool->cnt.high);
+
+		if (usage > high)
+			schedule_timeout_killable(HZ / 10);
+	}
+	rcu_read_unlock();
+
+	css_put(&dmemcs->css);
+}
+EXPORT_SYMBOL_GPL(__dmem_cgroup_handle_over_high);
+
 struct cgroup_subsys dmem_cgrp_subsys = {
 	.css_alloc	= dmemcs_alloc,
 	.css_free	= dmemcs_free,

---
base-commit: ab5fce87a778cb780a05984a2ca448f2b41aafbf
change-id: 20260519-feature-dmem-high-16997148dc38

Best regards,
-- 
Qiliang Yuan <realwujing@gmail.com>


^ permalink raw reply related

* Re: [PATCH 1/4] memcg: store node_id instead of pglist_data pointer
From: Harry Yoo @ 2026-05-20  6:01 UTC (permalink / raw)
  To: Shakeel Butt, Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Meta kernel team,
	linux-mm, cgroups, linux-kernel, kernel test robot
In-Reply-To: <20260520053123.2709959-2-shakeel.butt@linux.dev>



On 5/20/26 2:31 PM, Shakeel Butt wrote:
> The struct obj_stock_pcp stores a pointer to pglist_data for the slab
> stats cached on the cpu. On 64-bit machines, this costs 8 bytes. The
> pointer is not strictly required: NODE_DATA() can recover it from the
> node id. Replace cached_pgdat with int16_t node_id and use NUMA_NO_NODE
> as the "no stats cached" sentinel.
> 
> At the moment all the archs limit MAX_NUMNODES to 1024 so int16_t is
> plenty; a BUILD_BUG_ON() makes sure we notice if that ever changes.
> 
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Tested-by: kernel test robot <oliver.sang@intel.com>
> ---

Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org>

-- 
Cheers,
Harry / Hyeonggon


^ permalink raw reply

* [PATCH 4/4] memcg: multi objcg charge support
From: Shakeel Butt @ 2026-05-20  5:31 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Harry Yoo,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <20260520053123.2709959-1-shakeel.butt@linux.dev>

Commit 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg
per-node type") split a memcg's single obj_cgroup into one per NUMA
node so that reparenting LRU folios can take per-node lru locks. As a
side effect, the per-CPU obj_stock_pcp -- which caches exactly one
cached_objcg -- thrashes on workloads where threads of the same memcg
run on different NUMA nodes. The kernel test robot reported a 67.7%
regression on stress-ng.switch.ops_per_sec from this pattern.

Mirror the multi-slot pattern already used by memcg_stock_pcp: turn
nr_bytes and cached_objcg into NR_OBJ_STOCK-element arrays, scan all
slots on consume/refill/account, prefer empty slots when inserting,
and evict a random slot only when full. With multiple slots a CPU can
hold the per-node objcg variants of one memcg plus a few siblings
without ever forcing a drain.

A single int8_t index records which slot the cached slab stats belong
to; the stats are flushed on slot or pgdat change. With NR_OBJ_STOCK
= 5 the layout (verified with pahole) is:

  offset 0  : lock(1) + index(1) + node_id(2) + slab stats(4) = 8B
  offset 8  : nr_bytes[5]                                     = 10B
  offset 18 : padding                                         = 6B
  offset 24 : cached[5]                                       = 40B
  offset 64 : (line 2) work_struct + flags (cold)

so consume_obj_stock, refill_obj_stock and the slab account path each
touch exactly one 64-byte cache line on non-debug 64-bit builds.

Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202605121641.b6a60cb0-lkp@intel.com
Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type")
Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
Tested-by: kernel test robot <oliver.sang@intel.com>
---
 mm/memcontrol.c | 185 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 133 insertions(+), 52 deletions(-)

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 1ed27fd06850..52104cbb8e7c 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -150,14 +150,14 @@ static void obj_cgroup_release(struct percpu_ref *ref)
 	 * However, it can be PAGE_SIZE or (x * PAGE_SIZE).
 	 *
 	 * The following sequence can lead to it:
-	 * 1) CPU0: objcg == stock->cached_objcg
+	 * 1) CPU0: objcg cached in one of stock->cached[i]
 	 * 2) CPU1: we do a small allocation (e.g. 92 bytes),
 	 *          PAGE_SIZE bytes are charged
 	 * 3) CPU1: a process from another memcg is allocating something,
 	 *          the stock if flushed,
 	 *          objcg->nr_charged_bytes = PAGE_SIZE - 92
 	 * 5) CPU0: we do release this object,
-	 *          92 bytes are added to stock->nr_bytes
+	 *          92 bytes are added to stock->nr_bytes[i]
 	 * 6) CPU0: stock is flushed,
 	 *          92 bytes are added to objcg->nr_charged_bytes
 	 *
@@ -2017,13 +2017,25 @@ static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
 	.lock = INIT_LOCAL_TRYLOCK(lock),
 };
 
+/*
+ * NR_OBJ_STOCK is sized so the entire hot path of obj_stock_pcp
+ * (lock, accounting metadata, nr_bytes[] and cached[]) fits within a
+ * single 64-byte cache line on non-debug 64-bit builds. With 5 slots:
+ *   lock(1) + index(1) + node_id(2) + slab stats(4) + nr_bytes(10)
+ *   + pad(6) + cached(40) == 64 bytes.
+ * A CPU can thus consume/refill/account against five different objcgs
+ * (typically per-node variants of the same memcg) while incurring at
+ * most one cache miss on the stock.
+ */
+#define NR_OBJ_STOCK 5
 struct obj_stock_pcp {
 	local_trylock_t lock;
-	uint16_t nr_bytes;
-	struct obj_cgroup *cached_objcg;
+	int8_t index;
 	int16_t node_id;
 	int16_t nr_slab_reclaimable_b;
 	int16_t nr_slab_unreclaimable_b;
+	uint16_t nr_bytes[NR_OBJ_STOCK];
+	struct obj_cgroup *cached[NR_OBJ_STOCK];
 
 	struct work_struct work;
 	unsigned long flags;
@@ -2031,11 +2043,13 @@ struct obj_stock_pcp {
 
 static DEFINE_PER_CPU_ALIGNED(struct obj_stock_pcp, obj_stock) = {
 	.lock = INIT_LOCAL_TRYLOCK(lock),
+	.index = -1,
 	.node_id = NUMA_NO_NODE,
 };
 
 static DEFINE_MUTEX(percpu_charge_mutex);
 
+static void drain_obj_stock_slot(struct obj_stock_pcp *stock, int i);
 static void drain_obj_stock(struct obj_stock_pcp *stock);
 static bool obj_stock_flush_required(struct obj_stock_pcp *stock,
 				     struct mem_cgroup *root_memcg);
@@ -3153,12 +3167,13 @@ static void unlock_stock(struct obj_stock_pcp *stock)
 		local_unlock(&obj_stock.lock);
 }
 
-/* Call after __refill_obj_stock() to ensure stock->cached_objg == objcg */
+/* Call after __refill_obj_stock() so a slot for objcg exists in the stock */
 static void __account_obj_stock(struct obj_cgroup *objcg,
 				struct obj_stock_pcp *stock, int nr,
 				struct pglist_data *pgdat, enum node_stat_item idx)
 {
 	int16_t *bytes;
+	int i;
 
 	/*
 	 * Though at the moment MAX_NUMNODES <= 1024 in all archs but let's make
@@ -3167,29 +3182,39 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
 	 */
 	BUILD_BUG_ON(MAX_NUMNODES >= S16_MAX);
 
-	if (!stock || READ_ONCE(stock->cached_objcg) != objcg)
+	if (!stock)
+		goto direct;
+
+	for (i = 0; i < NR_OBJ_STOCK; ++i) {
+		if (READ_ONCE(stock->cached[i]) == objcg)
+			break;
+	}
+	if (i == NR_OBJ_STOCK)
 		goto direct;
 
 	/*
 	 * Save vmstat data in stock and skip vmstat array update unless
-	 * accumulating over a page of vmstat data or when pgdat changes.
+	 * accumulating over a page of vmstat data or when the objcg slot or
+	 * pgdat the stats belong to changes.
 	 */
-	if (stock->node_id == NUMA_NO_NODE) {
+	if (stock->index < 0) {
+		stock->index = i;
 		stock->node_id = pgdat->node_id;
-	} else if (stock->node_id != pgdat->node_id) {
-		/* Flush the existing cached vmstat data */
+	} else if (stock->index != i || stock->node_id != pgdat->node_id) {
+		struct obj_cgroup *old = READ_ONCE(stock->cached[stock->index]);
 		struct pglist_data *oldpg = NODE_DATA(stock->node_id);
 
 		if (stock->nr_slab_reclaimable_b) {
-			mod_objcg_mlstate(objcg, oldpg, NR_SLAB_RECLAIMABLE_B,
+			mod_objcg_mlstate(old, oldpg, NR_SLAB_RECLAIMABLE_B,
 					  stock->nr_slab_reclaimable_b);
 			stock->nr_slab_reclaimable_b = 0;
 		}
 		if (stock->nr_slab_unreclaimable_b) {
-			mod_objcg_mlstate(objcg, oldpg, NR_SLAB_UNRECLAIMABLE_B,
+			mod_objcg_mlstate(old, oldpg, NR_SLAB_UNRECLAIMABLE_B,
 					  stock->nr_slab_unreclaimable_b);
 			stock->nr_slab_unreclaimable_b = 0;
 		}
+		stock->index = i;
 		stock->node_id = pgdat->node_id;
 	}
 
@@ -3230,10 +3255,16 @@ static bool __consume_obj_stock(struct obj_cgroup *objcg,
 				struct obj_stock_pcp *stock,
 				unsigned int nr_bytes)
 {
-	if (objcg == READ_ONCE(stock->cached_objcg) &&
-	    stock->nr_bytes >= nr_bytes) {
-		stock->nr_bytes -= nr_bytes;
-		return true;
+	int i;
+
+	for (i = 0; i < NR_OBJ_STOCK; ++i) {
+		if (READ_ONCE(stock->cached[i]) != objcg)
+			continue;
+		if (stock->nr_bytes[i] >= nr_bytes) {
+			stock->nr_bytes[i] -= nr_bytes;
+			return true;
+		}
+		return false;
 	}
 
 	return false;
@@ -3254,16 +3285,42 @@ static bool consume_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes)
 	return ret;
 }
 
-static void drain_obj_stock(struct obj_stock_pcp *stock)
+/* Flush the cached slab stats (if any) back to their owning objcg/pgdat. */
+static void drain_obj_stock_stats(struct obj_stock_pcp *stock)
 {
-	struct obj_cgroup *old = READ_ONCE(stock->cached_objcg);
+	struct obj_cgroup *old;
+	struct pglist_data *oldpg;
+
+	if (stock->index < 0)
+		return;
+
+	old = READ_ONCE(stock->cached[stock->index]);
+	oldpg = NODE_DATA(stock->node_id);
+
+	if (stock->nr_slab_reclaimable_b) {
+		mod_objcg_mlstate(old, oldpg, NR_SLAB_RECLAIMABLE_B,
+				  stock->nr_slab_reclaimable_b);
+		stock->nr_slab_reclaimable_b = 0;
+	}
+	if (stock->nr_slab_unreclaimable_b) {
+		mod_objcg_mlstate(old, oldpg, NR_SLAB_UNRECLAIMABLE_B,
+				  stock->nr_slab_unreclaimable_b);
+		stock->nr_slab_unreclaimable_b = 0;
+	}
+	stock->index = -1;
+	stock->node_id = NUMA_NO_NODE;
+}
+
+static void drain_obj_stock_slot(struct obj_stock_pcp *stock, int i)
+{
+	struct obj_cgroup *old = READ_ONCE(stock->cached[i]);
 
 	if (!old)
 		return;
 
-	if (stock->nr_bytes) {
-		unsigned int nr_pages = stock->nr_bytes >> PAGE_SHIFT;
-		unsigned int nr_bytes = stock->nr_bytes & (PAGE_SIZE - 1);
+	if (stock->nr_bytes[i]) {
+		unsigned int nr_pages = stock->nr_bytes[i] >> PAGE_SHIFT;
+		unsigned int nr_bytes = stock->nr_bytes[i] & (PAGE_SIZE - 1);
 
 		if (nr_pages) {
 			struct mem_cgroup *memcg;
@@ -3289,46 +3346,43 @@ static void drain_obj_stock(struct obj_stock_pcp *stock)
 		 * so it might be changed in the future.
 		 */
 		atomic_add(nr_bytes, &old->nr_charged_bytes);
-		stock->nr_bytes = 0;
+		stock->nr_bytes[i] = 0;
 	}
 
-	/*
-	 * Flush the vmstat data in current stock
-	 */
-	if (stock->nr_slab_reclaimable_b || stock->nr_slab_unreclaimable_b) {
-		struct pglist_data *oldpg = NODE_DATA(stock->node_id);
-
-		if (stock->nr_slab_reclaimable_b) {
-			mod_objcg_mlstate(old, oldpg,
-					  NR_SLAB_RECLAIMABLE_B,
-					  stock->nr_slab_reclaimable_b);
-			stock->nr_slab_reclaimable_b = 0;
-		}
-		if (stock->nr_slab_unreclaimable_b) {
-			mod_objcg_mlstate(old, oldpg,
-					  NR_SLAB_UNRECLAIMABLE_B,
-					  stock->nr_slab_unreclaimable_b);
-			stock->nr_slab_unreclaimable_b = 0;
-		}
-		stock->node_id = NUMA_NO_NODE;
-	}
+	/* Flush vmstat data when its owning slot is being drained. */
+	if (stock->index == i)
+		drain_obj_stock_stats(stock);
 
-	WRITE_ONCE(stock->cached_objcg, NULL);
+	WRITE_ONCE(stock->cached[i], NULL);
 	obj_cgroup_put(old);
 }
 
+static void drain_obj_stock(struct obj_stock_pcp *stock)
+{
+	int i;
+
+	for (i = 0; i < NR_OBJ_STOCK; ++i)
+		drain_obj_stock_slot(stock, i);
+}
+
 static bool obj_stock_flush_required(struct obj_stock_pcp *stock,
 				     struct mem_cgroup *root_memcg)
 {
-	struct obj_cgroup *objcg = READ_ONCE(stock->cached_objcg);
+	struct obj_cgroup *objcg;
 	struct mem_cgroup *memcg;
 	bool flush = false;
+	int i;
 
 	rcu_read_lock();
-	if (objcg) {
+	for (i = 0; i < NR_OBJ_STOCK; ++i) {
+		objcg = READ_ONCE(stock->cached[i]);
+		if (!objcg)
+			continue;
 		memcg = obj_cgroup_memcg(objcg);
-		if (memcg && mem_cgroup_is_descendant(memcg, root_memcg))
+		if (memcg && mem_cgroup_is_descendant(memcg, root_memcg)) {
 			flush = true;
+			break;
+		}
 	}
 	rcu_read_unlock();
 
@@ -3342,6 +3396,7 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
 {
 	unsigned int nr_pages = 0;
 	unsigned int stock_nr_bytes;
+	int i, slot = -1, empty_slot = -1;
 
 	if (!stock) {
 		nr_pages = nr_bytes >> PAGE_SHIFT;
@@ -3350,19 +3405,45 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
 		goto out;
 	}
 
-	stock_nr_bytes = stock->nr_bytes;
-	if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */
-		drain_obj_stock(stock);
+	for (i = 0; i < NR_OBJ_STOCK; ++i) {
+		struct obj_cgroup *cached = READ_ONCE(stock->cached[i]);
+
+		if (!cached) {
+			if (empty_slot == -1)
+				empty_slot = i;
+			continue;
+		}
+		if (cached == objcg) {
+			slot = i;
+			break;
+		}
+	}
+
+	if (slot == -1) {
+		slot = empty_slot;
+		if (slot == -1) {
+			slot = get_random_u32_below(NR_OBJ_STOCK);
+			drain_obj_stock_slot(stock, slot);
+		}
 		obj_cgroup_get(objcg);
+		/*
+		 * Keep the xchg result in the unsigned int local; storing
+		 * it directly into stock->nr_bytes[slot] (uint16_t) would
+		 * silently truncate values >= U16_MAX and bypass the flush
+		 * guard below, leaking page-counter charges.
+		 */
 		stock_nr_bytes = atomic_read(&objcg->nr_charged_bytes)
 				? atomic_xchg(&objcg->nr_charged_bytes, 0) : 0;
-		WRITE_ONCE(stock->cached_objcg, objcg);
+		WRITE_ONCE(stock->cached[slot], objcg);
 
 		allow_uncharge = true;	/* Allow uncharge when objcg changes */
+	} else {
+		stock_nr_bytes = stock->nr_bytes[slot];
 	}
+
 	stock_nr_bytes += nr_bytes;
 
-	/* Since stock->nr_bytes is uint16_t, don't refill >= U16_MAX */
+	/* nr_bytes[] is uint16_t; flush if we would refill >= U16_MAX. */
 	if ((allow_uncharge && (stock_nr_bytes > PAGE_SIZE)) ||
 	    stock_nr_bytes >= U16_MAX) {
 		nr_pages = stock_nr_bytes >> PAGE_SHIFT;
@@ -3384,7 +3465,7 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
 			stock_nr_bytes = kept;
 		}
 	}
-	stock->nr_bytes = stock_nr_bytes;
+	stock->nr_bytes[slot] = stock_nr_bytes;
 
 out:
 	if (nr_pages)
-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH 3/4] memcg: int16_t for cached slab stats
From: Shakeel Butt @ 2026-05-20  5:31 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Harry Yoo,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <20260520053123.2709959-1-shakeel.butt@linux.dev>

Currently struct obj_stock_pcp stores cached slab stats in 'int' which
is 4 bytes per counter on 64-bit machines. Switch them to int16_t to
shrink the cached metadata.

The existing PAGE_SIZE flush in __account_obj_stock() bounds *bytes at
PAGE_SIZE on 4KiB and 16KiB page archs, well within int16_t. On 64KiB
pages PAGE_SIZE is well above S16_MAX so that flush never fires, and a
sufficiently long run of accumulations would overflow the cache. Add
an explicit S16_MAX guard before each add: when the next add would
push abs(*bytes) past S16_MAX, fold the cached value into @nr and
flush directly via mod_objcg_mlstate() before the accumulation.

Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
Tested-by: kernel test robot <oliver.sang@intel.com>
---
 mm/memcontrol.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index b3d63d9f267c..1ed27fd06850 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2022,8 +2022,8 @@ struct obj_stock_pcp {
 	uint16_t nr_bytes;
 	struct obj_cgroup *cached_objcg;
 	int16_t node_id;
-	int nr_slab_reclaimable_b;
-	int nr_slab_unreclaimable_b;
+	int16_t nr_slab_reclaimable_b;
+	int16_t nr_slab_unreclaimable_b;
 
 	struct work_struct work;
 	unsigned long flags;
@@ -3158,7 +3158,7 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
 				struct obj_stock_pcp *stock, int nr,
 				struct pglist_data *pgdat, enum node_stat_item idx)
 {
-	int *bytes;
+	int16_t *bytes;
 
 	/*
 	 * Though at the moment MAX_NUMNODES <= 1024 in all archs but let's make
@@ -3195,6 +3195,16 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
 
 	bytes = (idx == NR_SLAB_RECLAIMABLE_B) ? &stock->nr_slab_reclaimable_b
 					       : &stock->nr_slab_unreclaimable_b;
+	/*
+	 * To avoid overflow or underflow, flush directly if accumulating @nr
+	 * would push the cached value past S16_MAX.
+	 */
+	if (abs(nr + *bytes) >= S16_MAX) {
+		nr += *bytes;
+		*bytes = 0;
+		goto direct;
+	}
+
 	/*
 	 * Even for large object >= PAGE_SIZE, the vmstat data will still be
 	 * cached locally at least once before pushing it out.
-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH 2/4] memcg: uint16_t for nr_bytes in obj_stock_pcp
From: Shakeel Butt @ 2026-05-20  5:31 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Harry Yoo,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <20260520053123.2709959-1-shakeel.butt@linux.dev>

Currently struct obj_stock_pcp stores nr_bytes in an 'unsigned int'
which is 4 bytes on 64-bit machines. Switch the field to uint16_t to
shrink the per-CPU cache.

The kernel supports PAGE_SIZE_4KB, _8KB, _16KB, _32KB, _64KB and
_256KB (see HAVE_PAGE_SIZE_* in arch/Kconfig). After the
PAGE_SIZE-aligned flush in __refill_obj_stock(), the sub-page
remainder fits in uint16_t up through 64KiB pages where PAGE_SIZE - 1
== U16_MAX, but on 256KiB pages PAGE_SIZE - 1 == 0x3FFFF exceeds
U16_MAX. The accumulator also needs to stay within uint16_t between
page-aligned flushes on 64KiB pages where PAGE_SIZE itself is
U16_MAX + 1.

Accumulate the new total in an 'unsigned int' local, then:

  1. Flush whenever the accumulator would hit U16_MAX. Together with
     the existing allow_uncharge flush at PAGE_SIZE, this keeps the
     uint16_t safe on PAGE_SIZE <= 64KiB.

  2. On configs with PAGE_SHIFT > 16 (PAGE_SIZE_256KB on hexagon and
     powerpc 44x), push any sub-page remainder above U16_MAX into
     objcg->nr_charged_bytes via atomic_add before storing back, so
     the store cannot silently truncate. The PAGE_SHIFT > 16 guard
     folds the branch out at compile time on smaller page sizes.

Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
Tested-by: kernel test robot <oliver.sang@intel.com>
---
 mm/memcontrol.c | 33 +++++++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index d7c162946719..b3d63d9f267c 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2019,7 +2019,7 @@ static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
 
 struct obj_stock_pcp {
 	local_trylock_t lock;
-	unsigned int nr_bytes;
+	uint16_t nr_bytes;
 	struct obj_cgroup *cached_objcg;
 	int16_t node_id;
 	int nr_slab_reclaimable_b;
@@ -3331,6 +3331,7 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
 			       bool allow_uncharge)
 {
 	unsigned int nr_pages = 0;
+	unsigned int stock_nr_bytes;
 
 	if (!stock) {
 		nr_pages = nr_bytes >> PAGE_SHIFT;
@@ -3339,21 +3340,41 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
 		goto out;
 	}
 
+	stock_nr_bytes = stock->nr_bytes;
 	if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */
 		drain_obj_stock(stock);
 		obj_cgroup_get(objcg);
-		stock->nr_bytes = atomic_read(&objcg->nr_charged_bytes)
+		stock_nr_bytes = atomic_read(&objcg->nr_charged_bytes)
 				? atomic_xchg(&objcg->nr_charged_bytes, 0) : 0;
 		WRITE_ONCE(stock->cached_objcg, objcg);
 
 		allow_uncharge = true;	/* Allow uncharge when objcg changes */
 	}
-	stock->nr_bytes += nr_bytes;
+	stock_nr_bytes += nr_bytes;
+
+	/* Since stock->nr_bytes is uint16_t, don't refill >= U16_MAX */
+	if ((allow_uncharge && (stock_nr_bytes > PAGE_SIZE)) ||
+	    stock_nr_bytes >= U16_MAX) {
+		nr_pages = stock_nr_bytes >> PAGE_SHIFT;
+		stock_nr_bytes &= (PAGE_SIZE - 1);
+
+		/*
+		 * On configs with PAGE_SHIFT > 16 (PAGE_SIZE_256KB on
+		 * hexagon and powerpc 44x), the sub-page remainder can
+		 * still exceed U16_MAX. Push the excess back to
+		 * objcg->nr_charged_bytes so the store into uint16_t
+		 * cannot silently truncate; folded out at compile time
+		 * on smaller page sizes.
+		 */
+		if (PAGE_SHIFT > 16 && stock_nr_bytes > U16_MAX) {
+			unsigned int kept = stock_nr_bytes & U16_MAX;
 
-	if (allow_uncharge && (stock->nr_bytes > PAGE_SIZE)) {
-		nr_pages = stock->nr_bytes >> PAGE_SHIFT;
-		stock->nr_bytes &= (PAGE_SIZE - 1);
+			atomic_add(stock_nr_bytes - kept,
+				   &objcg->nr_charged_bytes);
+			stock_nr_bytes = kept;
+		}
 	}
+	stock->nr_bytes = stock_nr_bytes;
 
 out:
 	if (nr_pages)
-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH 1/4] memcg: store node_id instead of pglist_data pointer
From: Shakeel Butt @ 2026-05-20  5:31 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Harry Yoo,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <20260520053123.2709959-1-shakeel.butt@linux.dev>

The struct obj_stock_pcp stores a pointer to pglist_data for the slab
stats cached on the cpu. On 64-bit machines, this costs 8 bytes. The
pointer is not strictly required: NODE_DATA() can recover it from the
node id. Replace cached_pgdat with int16_t node_id and use NUMA_NO_NODE
as the "no stats cached" sentinel.

At the moment all the archs limit MAX_NUMNODES to 1024 so int16_t is
plenty; a BUILD_BUG_ON() makes sure we notice if that ever changes.

Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
Tested-by: kernel test robot <oliver.sang@intel.com>
---
 mm/memcontrol.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index b8caeb7ccaa3..d7c162946719 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2021,7 +2021,7 @@ struct obj_stock_pcp {
 	local_trylock_t lock;
 	unsigned int nr_bytes;
 	struct obj_cgroup *cached_objcg;
-	struct pglist_data *cached_pgdat;
+	int16_t node_id;
 	int nr_slab_reclaimable_b;
 	int nr_slab_unreclaimable_b;
 
@@ -2031,6 +2031,7 @@ struct obj_stock_pcp {
 
 static DEFINE_PER_CPU_ALIGNED(struct obj_stock_pcp, obj_stock) = {
 	.lock = INIT_LOCAL_TRYLOCK(lock),
+	.node_id = NUMA_NO_NODE,
 };
 
 static DEFINE_MUTEX(percpu_charge_mutex);
@@ -3159,6 +3160,13 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
 {
 	int *bytes;
 
+	/*
+	 * Though at the moment MAX_NUMNODES <= 1024 in all archs but let's make
+	 * sure it does not exceed S16_MAX otherwise we need to fix node_id type
+	 * in struct obj_stock_pcp.
+	 */
+	BUILD_BUG_ON(MAX_NUMNODES >= S16_MAX);
+
 	if (!stock || READ_ONCE(stock->cached_objcg) != objcg)
 		goto direct;
 
@@ -3166,9 +3174,11 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
 	 * Save vmstat data in stock and skip vmstat array update unless
 	 * accumulating over a page of vmstat data or when pgdat changes.
 	 */
-	if (stock->cached_pgdat != pgdat) {
+	if (stock->node_id == NUMA_NO_NODE) {
+		stock->node_id = pgdat->node_id;
+	} else if (stock->node_id != pgdat->node_id) {
 		/* Flush the existing cached vmstat data */
-		struct pglist_data *oldpg = stock->cached_pgdat;
+		struct pglist_data *oldpg = NODE_DATA(stock->node_id);
 
 		if (stock->nr_slab_reclaimable_b) {
 			mod_objcg_mlstate(objcg, oldpg, NR_SLAB_RECLAIMABLE_B,
@@ -3180,7 +3190,7 @@ static void __account_obj_stock(struct obj_cgroup *objcg,
 					  stock->nr_slab_unreclaimable_b);
 			stock->nr_slab_unreclaimable_b = 0;
 		}
-		stock->cached_pgdat = pgdat;
+		stock->node_id = pgdat->node_id;
 	}
 
 	bytes = (idx == NR_SLAB_RECLAIMABLE_B) ? &stock->nr_slab_reclaimable_b
@@ -3276,19 +3286,21 @@ static void drain_obj_stock(struct obj_stock_pcp *stock)
 	 * Flush the vmstat data in current stock
 	 */
 	if (stock->nr_slab_reclaimable_b || stock->nr_slab_unreclaimable_b) {
+		struct pglist_data *oldpg = NODE_DATA(stock->node_id);
+
 		if (stock->nr_slab_reclaimable_b) {
-			mod_objcg_mlstate(old, stock->cached_pgdat,
+			mod_objcg_mlstate(old, oldpg,
 					  NR_SLAB_RECLAIMABLE_B,
 					  stock->nr_slab_reclaimable_b);
 			stock->nr_slab_reclaimable_b = 0;
 		}
 		if (stock->nr_slab_unreclaimable_b) {
-			mod_objcg_mlstate(old, stock->cached_pgdat,
+			mod_objcg_mlstate(old, oldpg,
 					  NR_SLAB_UNRECLAIMABLE_B,
 					  stock->nr_slab_unreclaimable_b);
 			stock->nr_slab_unreclaimable_b = 0;
 		}
-		stock->cached_pgdat = NULL;
+		stock->node_id = NUMA_NO_NODE;
 	}
 
 	WRITE_ONCE(stock->cached_objcg, NULL);
-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH 0/4] memcg: shrink obj_stock_pcp and cache multiple objcgs
From: Shakeel Butt @ 2026-05-20  5:31 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Harry Yoo,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot

Commit 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg
per-node type") split a memcg's single obj_cgroup into one per NUMA
node so that reparenting LRU folios can take per-node lru locks. As a
side effect, the per-CPU obj_stock_pcp -- which caches a single
cached_objcg pointer -- thrashes on workloads where threads of the
same memcg run on different NUMA nodes. The kernel test robot reported
a 67.7% regression on stress-ng.switch.ops_per_sec from this pattern.

Commit d0211878ce06 ("memcg: cache obj_stock by memcg, not by objcg
pointer") landed as a temporary fix by treating sibling per-node
objcgs as equivalent for the cache lookup, intended to be reverted
once per-node kmem accounting is introduced. This series takes a more
general approach: cache multiple objcgs per CPU using the multi-slot
pattern memcg_stock_pcp already uses, so the per-node objcg variants
of one memcg can all coexist in the stock without ever forcing a
drain. The temporary fix can then be reverted.

To avoid increasing the per-CPU cache footprint, the first three
patches shrink the existing single-slot obj_stock_pcp fields.
The final patch converts cached_objcg and nr_bytes into
NR_OBJ_STOCK=5 slot arrays and reorders the struct so the entire
consume/refill/account hot path fits within a single 64-byte cache
line on non-debug 64-bit builds (verified with pahole).

Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202605121641.b6a60cb0-lkp@intel.com
Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type")
Tested-by: kernel test robot <oliver.sang@intel.com>

Shakeel Butt (4):
  memcg: store node_id instead of pglist_data pointer
  memcg: uint16_t for nr_bytes in obj_stock_pcp
  memcg: int16_t for cached slab stats
  memcg: multi objcg charge support

 mm/memcontrol.c | 214 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 157 insertions(+), 57 deletions(-)

--
2.53.0-Meta


^ permalink raw reply

* [PATCH 0/4] memcg: shrink obj_stock_pcp and cache multiple objcgs
From: Shakeel Butt @ 2026-05-20  5:29 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Johannes Weiner, Michal Hocko, Roman Gushchin, Muchun Song,
	Qi Zheng, Alexandre Ghiti, Joshua Hahn, Harry Yoo,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot

Commit 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg
per-node type") split a memcg's single obj_cgroup into one per NUMA
node so that reparenting LRU folios can take per-node lru locks. As a
side effect, the per-CPU obj_stock_pcp -- which caches a single
cached_objcg pointer -- thrashes on workloads where threads of the
same memcg run on different NUMA nodes. The kernel test robot reported
a 67.7% regression on stress-ng.switch.ops_per_sec from this pattern.

Commit d0211878ce06 ("memcg: cache obj_stock by memcg, not by objcg
pointer") landed as a temporary fix by treating sibling per-node
objcgs as equivalent for the cache lookup, intended to be reverted
once per-node kmem accounting is introduced. This series takes a more
general approach: cache multiple objcgs per CPU using the multi-slot
pattern memcg_stock_pcp already uses, so the per-node objcg variants
of one memcg can all coexist in the stock without ever forcing a
drain. The temporary fix can then be reverted.

To avoid increasing the per-CPU cache footprint, the first three
patches shrink the existing single-slot obj_stock_pcp fields.
The final patch converts cached_objcg and nr_bytes into
NR_OBJ_STOCK=5 slot arrays and reorders the struct so the entire
consume/refill/account hot path fits within a single 64-byte cache
line on non-debug 64-bit builds (verified with pahole).

Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202605121641.b6a60cb0-lkp@intel.com
Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type")
Tested-by: kernel test robot <oliver.sang@intel.com>

Shakeel Butt (4):
  memcg: store node_id instead of pglist_data pointer
  memcg: uint16_t for nr_bytes in obj_stock_pcp
  memcg: int16_t for cached slab stats
  memcg: multi objcg charge support

 mm/memcontrol.c | 214 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 157 insertions(+), 57 deletions(-)

--
2.53.0-Meta


^ permalink raw reply

* Re: [linus:master] [mm] 01b9da291c: stress-ng.switch.ops_per_sec 67.7% regression
From: Shakeel Butt @ 2026-05-20  4:12 UTC (permalink / raw)
  To: Oliver Sang
  Cc: Qi Zheng, oe-lkp, lkp, linux-kernel, Andrew Morton, David Carlier,
	Allen Pais, Axel Rasmussen, Baoquan He, Chengming Zhou,
	Chen Ridong, David Hildenbrand, Hamza Mahfooz, Harry Yoo,
	Hugh Dickins, Imran Khan, Johannes Weiner, Kamalesh Babulal,
	Lance Yang, Liam Howlett, Lorenzo Stoakes, Michal Hocko,
	Michal Koutný, Mike Rapoport, Muchun Song, Muchun Song,
	Nhat Pham, Roman Gushchin, Suren Baghdasaryan, Usama Arif,
	Vlastimil Babka, Wei Xu, Yosry Ahmed, Yuanchu Xie, Zi Yan,
	Usama Arif, cgroups, linux-mm
In-Reply-To: <ag0fyi8GjHHf8bdC@xsang-OptiPlex-9020>

On Wed, May 20, 2026 at 10:43:22AM +0800, Oliver Sang wrote:
> hi, Shakeel,
> 
> On Tue, May 19, 2026 at 07:22:52AM -0700, Shakeel Butt wrote:
> > Hi Oliver,
> > 
> 
> [...]
> 
> > 
> > > > > 
> > > > > Also I am rethinking the approach, so I will send a prototype in response on
> > > > > this email for which I will need your help in testing.
> > > > 
> > > > Hi Oliver, can you please test the following patch?
> > > 
> > > got it. will change to test following patch. and this looks quite different
> > > with v2 or v3, so if you still want us to test v3, please let me know. thanks!
> > > 
> > 
> > No need to test v3 as it is similar to v2. Please test the following patch as it
> > is a direction I want to pursue and wanted an early signal if this is the right
> > direction.
> 
> FYI. in our tests, the following patch also recovers the regresion.
> 
> Tested-by: kernel test robot <oliver.sang@intel.com>

Thanks a lot. I will send out the formal patch with your tested-by tag.

^ permalink raw reply

* [PATCH] cgroup: rstat: relax NMI guard after switch to try_cmpxchg
From: Cunlong Li @ 2026-05-20  3:30 UTC (permalink / raw)
  To: Tejun Heo, Johannes Weiner, Michal Koutný, Shakeel Butt
  Cc: cgroups, linux-kernel, Cunlong Li

Commit 36df6e3dbd7e ("cgroup: make css_rstat_updated nmi safe") used
this_cpu_cmpxchg() for the lockless insertion, and therefore required
both ARCH_HAVE_NMI_SAFE_CMPXCHG and ARCH_HAS_NMI_SAFE_THIS_CPU_OPS in
the NMI guard: on archs without the latter, this_cpu_cmpxchg() falls
back to "local_irq_save() + plain cmpxchg", and local_irq_save()
cannot mask NMIs.

Commit 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in
css_rstat_updated") later replaced this_cpu_cmpxchg() with plain
try_cmpxchg() to fix cross-CPU lockless-list corruption, but left the
NMI guard untouched.  After that switch, css_rstat_updated() no longer
performs any this_cpu_*() RMW operations and only relies on the arch
having NMI-safe cmpxchg, so ARCH_HAS_NMI_SAFE_THIS_CPU_OPS is no
longer required in the guard.

Relax the guard accordingly so that archs which have HAVE_NMI and
ARCH_HAVE_NMI_SAFE_CMPXCHG but not ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
(e.g. sparc, powerpc on PPC64/BOOK3S) can benefit from the existing
CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC path.  Without this, the css
is never queued in NMI on those archs, and the atomics staged by
account_{slab,kmem}_nmi_safe() are not drained by flush_nmi_stats().

Fixes: 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in css_rstat_updated")
Signed-off-by: Cunlong Li <shenxiaogll@gmail.com>
---
 kernel/cgroup/rstat.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c
index 150e5871e66f..fa46611098a5 100644
--- a/kernel/cgroup/rstat.c
+++ b/kernel/cgroup/rstat.c
@@ -83,11 +83,10 @@ __bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu)
 	lockdep_assert_preemption_disabled();
 
 	/*
-	 * For archs withnot nmi safe cmpxchg or percpu ops support, ignore
-	 * the requests from nmi context.
+	 * The lockless insertion below relies on NMI-safe cmpxchg;
+	 * bail out in NMI on archs that don't provide it.
 	 */
-	if ((!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) ||
-	     !IS_ENABLED(CONFIG_ARCH_HAS_NMI_SAFE_THIS_CPU_OPS)) && in_nmi())
+	if (!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) && in_nmi())
 		return;
 
 	rstatc = css_rstat_cpu(css, cpu);

---
base-commit: 27fa82620cbaa89a7fc11ac3057701d598813e87
change-id: 20260520-nmi-0493a1569716

Best regards,
-- 
Cunlong Li <shenxiaogll@gmail.com>


^ permalink raw reply related

* Re: [linus:master] [mm] 01b9da291c: stress-ng.switch.ops_per_sec 67.7% regression
From: Oliver Sang @ 2026-05-20  2:43 UTC (permalink / raw)
  To: Shakeel Butt
  Cc: Qi Zheng, oe-lkp, lkp, linux-kernel, Andrew Morton, David Carlier,
	Allen Pais, Axel Rasmussen, Baoquan He, Chengming Zhou,
	Chen Ridong, David Hildenbrand, Hamza Mahfooz, Harry Yoo,
	Hugh Dickins, Imran Khan, Johannes Weiner, Kamalesh Babulal,
	Lance Yang, Liam Howlett, Lorenzo Stoakes, Michal Hocko,
	Michal Koutný, Mike Rapoport, Muchun Song, Muchun Song,
	Nhat Pham, Roman Gushchin, Suren Baghdasaryan, Usama Arif,
	Vlastimil Babka, Wei Xu, Yosry Ahmed, Yuanchu Xie, Zi Yan,
	Usama Arif, cgroups, linux-mm, oliver.sang
In-Reply-To: <agxxuHOfNLX-32kI@linux.dev>

hi, Shakeel,

On Tue, May 19, 2026 at 07:22:52AM -0700, Shakeel Butt wrote:
> Hi Oliver,
> 

[...]

> 
> > > > 
> > > > Also I am rethinking the approach, so I will send a prototype in response on
> > > > this email for which I will need your help in testing.
> > > 
> > > Hi Oliver, can you please test the following patch?
> > 
> > got it. will change to test following patch. and this looks quite different
> > with v2 or v3, so if you still want us to test v3, please let me know. thanks!
> > 
> 
> No need to test v3 as it is similar to v2. Please test the following patch as it
> is a direction I want to pursue and wanted an early signal if this is the right
> direction.

FYI. in our tests, the following patch also recovers the regresion.

Tested-by: kernel test robot <oliver.sang@intel.com>

=========================================================================================
compiler/cpufreq_governor/kconfig/method/nr_threads/rootfs/tbox_group/test/testcase/testtime:
  gcc-14/performance/x86_64-rhel-9.4/mq/100%/debian-13-x86_64-20250902.cgz/lkp-spr-r02/switch/stress-ng/60s

commit: 
  8285917d6f ("mm: memcontrol: prepare for reparenting non-hierarchical stats")
  01b9da291c ("mm: memcontrol: convert objcg to be per-memcg per-node type")
  542b7317fe ("memcg: shrink obj_stock_pcp and cache multiple objcgs")

8285917d6f383aef 01b9da291c4969354807b52956f 542b7317fe304742e79a9a2215e
---------------- --------------------------- ---------------------------
         %stddev     %change         %stddev     %change         %stddev
             \          |                \          |                \
      5849          +210.2%      18145 ±  3%      +1.7%       5949        stress-ng.switch.nanosecs_per_context_switch_mq_method
 2.296e+09           -67.7%  7.408e+08 ±  3%      -1.7%  2.257e+09        stress-ng.switch.ops
  38288993           -67.7%   12355813 ±  3%      -1.7%   37646995        stress-ng.switch.ops_per_sec
  93416932           -68.6%   29310048 ±  3%      -0.7%   92764788        stress-ng.time.involuntary_context_switches
     15845           +11.0%      17584            +0.2%      15882        stress-ng.time.percent_of_cpu_this_job_got
      8556           +18.2%      10115            +0.4%       8589        stress-ng.time.system_time
    963.36           -53.5%     447.72 ±  3%      -1.1%     952.66        stress-ng.time.user_time
 1.518e+09           -69.7%  4.607e+08 ±  2%      -1.6%  1.494e+09        stress-ng.time.voluntary_context_switches
      1124 ± 17%     +34.3%       1509 ±  8%      -4.0%       1079 ± 27%  perf-c2c.HITM.remote
  2.55e+09           -12.3%  2.236e+09 ±  3%      +5.3%  2.685e+09 ±  6%  cpuidle..time
  8.29e+08           -71.8%  2.337e+08 ±  2%      -2.1%   8.12e+08        cpuidle..usage
  14184409 ±  2%     -16.4%   11860068            +0.5%   14258186        vmstat.memory.cache
  39204964           -69.7%   11868752 ±  2%      -2.9%   38078258        vmstat.system.cs
   1808848           -38.5%    1111830            -0.6%    1798192        vmstat.system.in
    389109 ± 23%     -20.4%     309611 ± 49%     +27.8%     497404 ±  8%  numa-numastat.node0.local_node
    504867 ±  9%     -13.8%     435237 ± 27%     +23.0%     621221 ± 10%  numa-numastat.node0.numa_hit
   4102960 ±  5%     -19.0%    3324393 ±  4%      -2.5%    3998654        numa-numastat.node1.local_node
   4218983 ±  3%     -18.7%    3430325 ±  3%      -2.6%    4107779 ±  2%  numa-numastat.node1.numa_hit
  93416932           -68.6%   29310048 ±  3%      -0.7%   92764788        time.involuntary_context_switches
     15845           +11.0%      17584            +0.2%      15882        time.percent_of_cpu_this_job_got
      8556           +18.2%      10115            +0.4%       8589        time.system_time
    963.36           -53.5%     447.72 ±  3%      -1.1%     952.66        time.user_time
 1.518e+09           -69.7%  4.607e+08 ±  2%      -1.6%  1.494e+09        time.voluntary_context_switches
     22.48            -4.4       18.08            +0.5       22.97 ±  4%  mpstat.cpu.all.idle%
      1.13            -0.4        0.73            -0.0        1.12        mpstat.cpu.all.irq%
      0.10            -0.0        0.09            -0.0        0.09        mpstat.cpu.all.soft%
     67.98            +9.1       77.06            -0.3       67.69        mpstat.cpu.all.sys%
      8.32            -4.3        4.04 ±  2%      -0.2        8.14        mpstat.cpu.all.usr%
     17.33 ±  2%     +15.4%      20.00 ±  4%      +2.4%      17.75 ±  4%  mpstat.max_utilization.seconds
  10939677 ±  2%     -21.0%    8641166            +0.8%   11022621 ±  2%  meminfo.Active
  10939661 ±  2%     -21.0%    8641149            +0.8%   11022605 ±  2%  meminfo.Active(anon)
  13917673 ±  2%     -16.4%   11633722            +0.6%   14000937        meminfo.Cached
  14400924 ±  2%     -16.0%   12102150            +0.3%   14443477        meminfo.Committed_AS
   8394752 ±  5%     +16.7%    9796949 ±  8%     +10.4%    9266688 ±  5%  meminfo.DirectMap2M
    617671           -12.0%     543559            +3.0%     636127 ±  3%  meminfo.Mapped
  18364992           -12.5%   16065468            +0.3%   18427193        meminfo.Memused
  10124702 ±  2%     -22.6%    7839682            +0.8%   10208910 ±  2%  meminfo.Shmem
  18393665           -12.5%   16100473            +0.4%   18462892        meminfo.max_used_kB
     60023 ± 85%     +48.1%      88868 ±  8%    +103.5%     122159 ± 17%  numa-meminfo.node0.Mapped
   1370772 ±124%    +153.5%    3474876          +175.9%    3782461        numa-meminfo.node0.Unevictable
  10552401 ±  4%     -23.3%    8092823            +1.9%   10752961        numa-meminfo.node1.Active
  10552392 ±  4%     -23.3%    8092820            +1.9%   10752957        numa-meminfo.node1.Active(anon)
     89021 ± 81%      -1.6%      87590 ± 71%    +108.4%     185539 ±  2%  numa-meminfo.node1.AnonHugePages
  12454155 ± 15%     -34.9%    8106052           -18.8%   10108844 ±  2%  numa-meminfo.node1.FilePages
     20917 ±  9%      -3.6%      20156 ±  9%      +8.5%      22693 ±  6%  numa-meminfo.node1.KernelStack
    559046 ±  8%     -19.2%     451929 ±  2%      -7.8%     515612 ±  4%  numa-meminfo.node1.Mapped
  14688311 ± 13%     -30.0%   10285394 ±  2%     -15.7%   12377651 ±  3%  numa-meminfo.node1.MemUsed
  10028979 ±  3%     -22.4%    7783864            +0.7%   10096369 ±  2%  numa-meminfo.node1.Shmem
   2425126 ± 70%     -86.7%     322088           -99.5%      12421 ±122%  numa-meminfo.node1.Unevictable
     10.59            -7.6        2.97 ±  8%      +0.1       10.69        turbostat.C1%
      0.85 ±  3%      +9.1        9.96 ±  2%      +0.0        0.87 ±  3%  turbostat.C1E%
      1.29 ±  6%     +19.4%       1.54 ±  2%      -1.4%       1.27 ± 16%  turbostat.CPU%c1
     48.67 ±  2%     -15.1%      41.33 ±  3%      -4.5%      46.50 ±  2%  turbostat.CoreTmp
      0.56           -60.7%       0.22 ±  3%      +0.9%       0.56        turbostat.IPC
 1.153e+08           -38.7%   70680365            +0.6%   1.16e+08        turbostat.IRQ
  10242404           -14.8%    8723704            -0.3%   10215225        turbostat.NMI
     88.65           -84.0        4.67 ± 33%      -2.9       85.77 ±  2%  turbostat.PKG_%
      3.82            -3.8        0.04 ± 10%      -0.2        3.60 ±  2%  turbostat.POLL%
     48.67 ±  2%     -13.7%      42.00 ±  3%      -4.5%      46.50 ±  2%  turbostat.PkgTmp
    683.77           -13.1%     594.00            -0.2%     682.36        turbostat.PkgWatt
     18.74            -3.3%      18.13            -0.6%      18.62        turbostat.RAMWatt
   2735312 ±  2%     -21.0%    2160742            +0.8%    2756101 ±  2%  proc-vmstat.nr_active_anon
    204708            -1.6%     201435            -0.1%     204540        proc-vmstat.nr_anon_pages
   3479812 ±  2%     -16.4%    2908863            +0.6%    3500665        proc-vmstat.nr_file_pages
    154477           -12.0%     135959            +3.0%     159129 ±  3%  proc-vmstat.nr_mapped
   2531568 ±  2%     -22.6%    1960353            +0.8%    2552660 ±  2%  proc-vmstat.nr_shmem
     42010            -3.5%      40543            +0.7%      42288        proc-vmstat.nr_slab_reclaimable
   2735312 ±  2%     -21.0%    2160742            +0.8%    2756101 ±  2%  proc-vmstat.nr_zone_active_anon
    210167 ±  5%     -11.5%     185950 ± 11%      -6.6%     196220 ±  5%  proc-vmstat.numa_hint_faults
   4730338 ±  2%     -18.2%    3871343            +0.1%    4733860        proc-vmstat.numa_hit
   4498551 ±  2%     -19.1%    3639783            +0.1%    4500902 ±  2%  proc-vmstat.numa_local
   4808959 ±  2%     -17.8%    3954157            -0.3%    4796169        proc-vmstat.pgalloc_normal
    806619            -5.1%     765525 ±  2%      +1.1%     815650 ±  2%  proc-vmstat.pgfault
     34098 ±  3%     -14.8%      29054            +0.7%      34328 ±  8%  proc-vmstat.pgreuse
     15033 ± 85%     +47.8%      22227 ±  8%    +103.3%      30562 ± 17%  numa-vmstat.node0.nr_mapped
    342693 ±124%    +153.5%     868719          +175.9%     945615        numa-vmstat.node0.nr_unevictable
    342693 ±124%    +153.5%     868719          +175.9%     945615        numa-vmstat.node0.nr_zone_unevictable
    505319 ±  9%     -14.0%     434390 ± 27%     +23.1%     622018 ± 10%  numa-vmstat.node0.numa_hit
    389561 ± 23%     -20.7%     308764 ± 50%     +27.9%     498200 ±  8%  numa-vmstat.node0.numa_local
   2638537 ±  4%     -23.3%    2022639            +1.9%    2688595        numa-vmstat.node1.nr_active_anon
   3113944 ± 15%     -34.9%    2025946           -18.8%    2527547 ±  2%  numa-vmstat.node1.nr_file_pages
     20917 ±  9%      -3.6%      20157 ±  9%      +8.5%      22691 ±  6%  numa-vmstat.node1.nr_kernel_stack
    139848 ±  9%     -19.3%     112912 ±  2%      -7.8%     128986 ±  4%  numa-vmstat.node1.nr_mapped
   2507650 ±  3%     -22.4%    1945399            +0.7%    2524428 ±  2%  numa-vmstat.node1.nr_shmem
    606281 ± 70%     -86.7%      80522           -99.5%       3105 ±122%  numa-vmstat.node1.nr_unevictable
   2638531 ±  4%     -23.3%    2022634            +1.9%    2688587        numa-vmstat.node1.nr_zone_active_anon
    606281 ± 70%     -86.7%      80522           -99.5%       3105 ±122%  numa-vmstat.node1.nr_zone_unevictable
   4219206 ±  3%     -18.7%    3430093 ±  3%      -2.6%    4108044 ±  2%  numa-vmstat.node1.numa_hit
   4103183 ±  4%     -19.0%    3324161 ±  4%      -2.5%    3998968        numa-vmstat.node1.numa_local
      0.11           +59.9%       0.17 ±  3%      -0.9%       0.11 ±  2%  perf-stat.i.MPKI
 6.653e+10           -61.7%  2.546e+10 ±  2%      +0.2%  6.669e+10        perf-stat.i.branch-instructions
      0.76            +0.1        0.89            +0.1        0.82        perf-stat.i.branch-miss-rate%
 4.685e+08           -59.7%  1.888e+08 ±  2%      +8.4%  5.079e+08        perf-stat.i.branch-misses
      1.12            +0.6        1.76 ±  3%      +0.0        1.13 ±  3%  perf-stat.i.cache-miss-rate%
  35553724 ±  3%     -40.4%   21188697            -0.8%   35253639 ±  2%  perf-stat.i.cache-misses
 4.194e+09           -68.3%  1.331e+09 ±  2%      -1.6%  4.129e+09        perf-stat.i.cache-references
  40710745           -69.6%   12395879 ±  2%      -1.5%   40099753        perf-stat.i.context-switches
      1.84          +189.1%       5.31 ±  2%      -0.5%       1.83        perf-stat.i.cpi
 5.965e+11            -2.0%  5.848e+11            -0.2%  5.956e+11        perf-stat.i.cpu-cycles
   8813175           -64.5%    3125097 ±  2%      -1.3%    8695118        perf-stat.i.cpu-migrations
     24447 ±  3%     +68.5%      41184 ±  2%      -0.4%      24353        perf-stat.i.cycles-between-cache-misses
 3.374e+11           -61.8%  1.287e+11 ±  2%      +0.1%  3.377e+11        perf-stat.i.instructions
      0.57           -60.8%       0.22 ±  2%      +0.3%       0.57        perf-stat.i.ipc
    221.10           -68.6%      69.32 ±  2%      -1.5%     217.85        perf-stat.i.metric.K/sec
     11782 ±  3%      -6.1%      11068 ±  3%      -2.7%      11463 ±  2%  perf-stat.i.minor-faults
     11782 ±  3%      -6.1%      11068 ±  3%      -2.7%      11463 ±  2%  perf-stat.i.page-faults
      0.10 ±  2%     +59.2%       0.17 ±  3%      -0.8%       0.10        perf-stat.overall.MPKI
      0.71            +0.0        0.75            +0.1        0.77        perf-stat.overall.branch-miss-rate%
      0.83 ±  3%      +0.7        1.56 ±  3%      +0.0        0.83        perf-stat.overall.cache-miss-rate%
      1.78          +162.2%       4.67 ±  2%      -0.4%       1.78        perf-stat.overall.cpi
     17181 ±  3%     +64.6%      28283            +0.4%      17248        perf-stat.overall.cycles-between-cache-misses
      0.56           -61.8%       0.21 ±  2%      +0.4%       0.56        perf-stat.overall.ipc
 6.388e+10           -62.3%  2.409e+10 ±  2%      +0.8%  6.439e+10        perf-stat.ps.branch-instructions
 4.538e+08           -60.0%  1.817e+08 ±  2%      +8.9%  4.941e+08        perf-stat.ps.branch-misses
  33674051 ±  3%     -40.1%   20155290            -0.2%   33597494        perf-stat.ps.cache-misses
 4.077e+09           -68.2%  1.296e+09 ±  2%      -1.2%  4.027e+09        perf-stat.ps.cache-references
  39570629           -69.5%   12072702 ±  2%      -1.2%   39106938        perf-stat.ps.context-switches
  5.78e+11            -1.4%    5.7e+11            +0.2%  5.793e+11        perf-stat.ps.cpu-cycles
   8584979           -64.5%    3051930 ±  2%      -1.0%    8495000        perf-stat.ps.cpu-migrations
 3.243e+11           -62.4%   1.22e+11 ±  2%      +0.6%  3.264e+11        perf-stat.ps.instructions
     11022 ±  4%      -6.5%      10300 ±  3%      -1.8%      10820        perf-stat.ps.minor-faults
     11022 ±  4%      -6.5%      10300 ±  3%      -1.8%      10821        perf-stat.ps.page-faults
 1.941e+13           -61.9%  7.405e+12 ±  3%      +2.3%  1.986e+13 ±  2%  perf-stat.total.instructions
     18451            +9.9%      20272            -0.5%      18360        sched_debug.cfs_rq:/.avg_vruntime.avg
      5869 ±  4%      -7.4%       5437 ±  5%     -10.1%       5278 ±  2%  sched_debug.cfs_rq:/.avg_vruntime.stddev
      0.68 ±  2%     -12.9%       0.59 ±  3%      -2.7%       0.66 ±  4%  sched_debug.cfs_rq:/.h_nr_queued.stddev
      0.62 ±  6%     -12.9%       0.54 ±  2%      -3.3%       0.60 ±  4%  sched_debug.cfs_rq:/.h_nr_runnable.stddev
      8469           +12.7%       9544 ±  2%      +1.3%       8581 ±  3%  sched_debug.cfs_rq:/.left_deadline.stddev
      8467           +12.7%       9542 ±  2%      +1.3%       8579 ±  3%  sched_debug.cfs_rq:/.left_vruntime.stddev
   3513124 ± 25%     -30.0%    2459550 ± 10%     -13.8%    3028548 ± 14%  sched_debug.cfs_rq:/.load.max
    588329 ±  5%     -11.2%     522578 ±  5%      +0.6%     591849 ±  3%  sched_debug.cfs_rq:/.load.stddev
     50699 ± 17%     -19.8%      40655 ±  7%     -24.8%      38144 ±  5%  sched_debug.cfs_rq:/.load_avg.max
      7968 ± 26%      -9.9%       7176 ± 15%     -30.7%       5519 ±  9%  sched_debug.cfs_rq:/.load_avg.stddev
      0.68 ±  2%     -12.9%       0.59 ±  3%      -2.8%       0.66 ±  4%  sched_debug.cfs_rq:/.nr_queued.stddev
     38.80 ± 32%    +108.5%      80.90 ± 16%     -14.7%      33.09 ± 15%  sched_debug.cfs_rq:/.removed.load_avg.avg
    857.83 ± 12%     +61.0%       1381 ± 12%      +1.4%     870.00 ± 17%  sched_debug.cfs_rq:/.removed.load_avg.max
    152.02 ± 18%     +57.2%     239.02 ± 11%      -8.0%     139.91 ±  8%  sched_debug.cfs_rq:/.removed.load_avg.stddev
     26.08 ± 28%    +143.0%      63.37 ± 14%      -6.3%      24.43 ± 18%  sched_debug.cfs_rq:/.removed.runnable_avg.avg
    547.00 ± 13%     +88.7%       1032 ± 12%      +2.7%     561.75 ± 22%  sched_debug.cfs_rq:/.removed.runnable_avg.max
     94.86 ± 17%     +84.3%     174.82 ±  9%      -2.9%      92.15 ±  9%  sched_debug.cfs_rq:/.removed.runnable_avg.stddev
      9.09 ± 52%    +253.3%      32.11 ± 17%     -34.3%       5.97 ± 15%  sched_debug.cfs_rq:/.removed.util_avg.avg
    275.17 ±  3%    +130.3%     633.67 ±  9%      +0.5%     276.62        sched_debug.cfs_rq:/.removed.util_avg.max
     44.90 ± 30%    +126.4%     101.66 ± 11%     -13.2%      38.95 ±  8%  sched_debug.cfs_rq:/.removed.util_avg.stddev
      8467           +12.7%       9542 ±  2%      +1.3%       8579 ±  3%  sched_debug.cfs_rq:/.right_vruntime.stddev
    659.63 ±  3%     +13.0%     745.47            -1.3%     651.21        sched_debug.cfs_rq:/.runnable_avg.avg
    271.34 ±  2%     +31.2%     355.98 ±  3%      -3.1%     262.84 ±  4%  sched_debug.cfs_rq:/.runnable_avg.stddev
      0.00 ± 26%    +110.4%       0.00 ± 45%      +1.8%       0.00 ± 32%  sched_debug.cfs_rq:/.spread.avg
      0.01 ± 13%    +174.3%       0.02 ± 25%     -28.3%       0.00 ± 26%  sched_debug.cfs_rq:/.spread.max
      0.00 ±  7%    +146.2%       0.00 ± 27%     -13.4%       0.00 ± 26%  sched_debug.cfs_rq:/.spread.stddev
    431.00           +14.5%     493.62            -2.7%     419.48        sched_debug.cfs_rq:/.util_avg.avg
      1061 ±  3%     +26.4%       1341 ±  3%      -0.6%       1055 ±  2%  sched_debug.cfs_rq:/.util_avg.max
    151.53 ±  5%     +50.1%     227.46 ±  2%      -9.2%     137.60 ±  5%  sched_debug.cfs_rq:/.util_avg.stddev
    206.96           +17.5%     243.18 ±  3%      +8.4%     224.27 ±  8%  sched_debug.cfs_rq:/.util_est.avg
     18451            +9.9%      20272            -0.5%      18360        sched_debug.cfs_rq:/.zero_vruntime.avg
      5869 ±  4%      -7.4%       5437 ±  5%     -10.1%       5278 ±  2%  sched_debug.cfs_rq:/.zero_vruntime.stddev
      2345           +33.6%       3133 ±  5%     +20.6%       2829 ± 11%  sched_debug.cpu.avg_idle.min
     13.18 ±  2%     +39.8%      18.42 ±  6%      +4.1%      13.72        sched_debug.cpu.clock.stddev
      3961           +14.6%       4541            +6.0%       4197 ±  4%  sched_debug.cpu.curr->pid.avg
      3213           -15.4%       2718            -5.8%       3028 ±  3%  sched_debug.cpu.curr->pid.stddev
      0.00 ± 29%    +157.3%       0.00 ± 35%    +101.7%       0.00 ± 51%  sched_debug.cpu.next_balance.stddev
      0.70           -15.8%       0.59 ±  3%      -5.0%       0.66 ±  2%  sched_debug.cpu.nr_running.stddev
   5474800           -69.7%    1660250 ±  2%      -1.6%    5386873        sched_debug.cpu.nr_switches.avg
   5648642           -65.5%    1946319 ±  5%      -1.5%    5561645        sched_debug.cpu.nr_switches.max
   2229198 ±  8%     -67.1%     734011 ± 20%      +9.1%    2431460 ± 15%  sched_debug.cpu.nr_switches.min
    297592 ±  6%     -25.9%     220513 ± 18%      -3.5%     287202 ±  9%  sched_debug.cpu.nr_switches.stddev
     23.75           -10.9       12.88            -6.3       17.48 ± 57%  perf-profile.calltrace.cycles-pp.common_startup_64
     23.65           -10.8       12.82            -6.2       17.41 ± 57%  perf-profile.calltrace.cycles-pp.start_secondary.common_startup_64
     23.62           -10.8       12.81            -6.2       17.39 ± 57%  perf-profile.calltrace.cycles-pp.cpu_startup_entry.start_secondary.common_startup_64
     23.51           -10.8       12.76            -6.2       17.30 ± 57%  perf-profile.calltrace.cycles-pp.do_idle.cpu_startup_entry.start_secondary.common_startup_64
     12.93            -7.0        5.94 ±  4%      -3.3        9.65 ± 57%  perf-profile.calltrace.cycles-pp.wake_up_q.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
     12.78            -6.9        5.89 ±  4%      -3.2        9.54 ± 57%  perf-profile.calltrace.cycles-pp.try_to_wake_up.wake_up_q.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64
     11.30            -5.2        6.07 ±  3%      -3.0        8.34 ± 57%  perf-profile.calltrace.cycles-pp.wake_up_q.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64.entry_SYSCALL_64_after_hwframe
     11.17            -5.2        6.02 ±  3%      -2.9        8.24 ± 57%  perf-profile.calltrace.cycles-pp.try_to_wake_up.wake_up_q.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64
      9.89            -4.8        5.08 ±  4%      -2.6        7.32 ± 57%  perf-profile.calltrace.cycles-pp.select_idle_sibling.select_task_rq_fair.select_task_rq.try_to_wake_up.wake_up_q
      4.52            -4.5        0.00            -1.3        3.21 ± 57%  perf-profile.calltrace.cycles-pp.poll_idle.cpuidle_enter_state.cpuidle_enter.cpuidle_idle_call.do_idle
     12.41            -4.4        7.96            -3.3        9.09 ± 57%  perf-profile.calltrace.cycles-pp.cpuidle_idle_call.do_idle.cpu_startup_entry.start_secondary.common_startup_64
      9.19            -4.4        4.77 ±  4%      -2.4        6.79 ± 57%  perf-profile.calltrace.cycles-pp.select_idle_cpu.select_idle_sibling.select_task_rq_fair.select_task_rq.try_to_wake_up
     11.29            -4.0        7.29            -3.0        8.27 ± 57%  perf-profile.calltrace.cycles-pp.cpuidle_enter_state.cpuidle_enter.cpuidle_idle_call.do_idle.cpu_startup_entry
     11.38            -4.0        7.40            -3.0        8.34 ± 57%  perf-profile.calltrace.cycles-pp.cpuidle_enter.cpuidle_idle_call.do_idle.cpu_startup_entry.start_secondary
      8.04            -3.9        4.13 ±  4%      -2.1        5.94 ± 57%  perf-profile.calltrace.cycles-pp.select_idle_core.select_idle_cpu.select_idle_sibling.select_task_rq_fair.select_task_rq
      6.91            -3.8        3.08 ±  4%      -1.8        5.15 ± 57%  perf-profile.calltrace.cycles-pp.select_task_rq.try_to_wake_up.wake_up_q.do_mq_timedreceive.__x64_sys_mq_timedreceive
      6.76            -3.8        3.00 ±  4%      -1.7        5.04 ± 57%  perf-profile.calltrace.cycles-pp.select_task_rq_fair.select_task_rq.try_to_wake_up.wake_up_q.do_mq_timedreceive
      8.71            -3.7        5.00            -2.2        6.52 ± 57%  perf-profile.calltrace.cycles-pp.wq_sleep.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64.entry_SYSCALL_64_after_hwframe
      5.71            -3.4        2.26 ±  4%      -1.5        4.21 ± 57%  perf-profile.calltrace.cycles-pp.flush_smp_call_function_queue.do_idle.cpu_startup_entry.start_secondary.common_startup_64
      8.12            -3.4        4.72            -2.1        6.07 ± 57%  perf-profile.calltrace.cycles-pp.schedule_hrtimeout_range_clock.wq_sleep.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64
      7.76            -3.2        4.55            -2.0        5.80 ± 57%  perf-profile.calltrace.cycles-pp.schedule.schedule_hrtimeout_range_clock.wq_sleep.do_mq_timedsend.__x64_sys_mq_timedsend
      8.39            -3.1        5.26            -2.1        6.26 ± 57%  perf-profile.calltrace.cycles-pp.wq_sleep.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
      7.47            -3.1        4.35            -1.9        5.59 ± 57%  perf-profile.calltrace.cycles-pp.__schedule.schedule.schedule_hrtimeout_range_clock.wq_sleep.do_mq_timedsend
      4.92            -2.9        2.00 ±  4%      -1.3        3.62 ± 57%  perf-profile.calltrace.cycles-pp.__flush_smp_call_function_queue.flush_smp_call_function_queue.do_idle.cpu_startup_entry.start_secondary
      7.79            -2.8        4.97            -2.0        5.79 ± 57%  perf-profile.calltrace.cycles-pp.schedule_hrtimeout_range_clock.wq_sleep.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64
      7.48            -2.7        4.79            -1.9        5.57 ± 57%  perf-profile.calltrace.cycles-pp.schedule.schedule_hrtimeout_range_clock.wq_sleep.do_mq_timedreceive.__x64_sys_mq_timedreceive
      7.17            -2.6        4.60            -1.8        5.34 ± 57%  perf-profile.calltrace.cycles-pp.__schedule.schedule.schedule_hrtimeout_range_clock.wq_sleep.do_mq_timedreceive
      5.54            -2.3        3.24 ±  4%      -1.5        4.05 ± 57%  perf-profile.calltrace.cycles-pp.select_task_rq.try_to_wake_up.wake_up_q.do_mq_timedsend.__x64_sys_mq_timedsend
      5.41            -2.2        3.16 ±  4%      -1.5        3.95 ± 57%  perf-profile.calltrace.cycles-pp.select_task_rq_fair.select_task_rq.try_to_wake_up.wake_up_q.do_mq_timedsend
      3.88            -2.2        1.67 ±  4%      -1.0        2.86 ± 57%  perf-profile.calltrace.cycles-pp.sched_ttwu_pending.__flush_smp_call_function_queue.flush_smp_call_function_queue.do_idle.cpu_startup_entry
      4.04            -2.2        1.88            -1.0        3.00 ± 57%  perf-profile.calltrace.cycles-pp.schedule_idle.do_idle.cpu_startup_entry.start_secondary.common_startup_64
      3.44            -2.2        1.28 ±  3%      -0.9        2.54 ± 57%  perf-profile.calltrace.cycles-pp.store_msg.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
      3.84            -2.0        1.80            -1.0        2.84 ± 57%  perf-profile.calltrace.cycles-pp.__schedule.schedule_idle.do_idle.cpu_startup_entry.start_secondary
      4.83            -2.0        2.85            -1.2        3.62 ± 57%  perf-profile.calltrace.cycles-pp.try_to_block_task.__schedule.schedule.schedule_hrtimeout_range_clock.wq_sleep
      4.71            -1.9        2.78            -1.2        3.53 ± 57%  perf-profile.calltrace.cycles-pp.dequeue_task_fair.try_to_block_task.__schedule.schedule.schedule_hrtimeout_range_clock
      1.84            -1.8        0.00            -0.5        1.35 ± 57%  perf-profile.calltrace.cycles-pp.wake_affine.select_task_rq_fair.select_task_rq.try_to_wake_up.wake_up_q
      4.39            -1.8        2.58            -1.1        3.30 ± 57%  perf-profile.calltrace.cycles-pp.dequeue_entities.dequeue_task_fair.try_to_block_task.__schedule.schedule
      2.37            -1.5        0.84 ±  3%      -0.6        1.76 ± 57%  perf-profile.calltrace.cycles-pp._copy_to_user.store_msg.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64
      2.66            -1.4        1.26 ±  4%      -0.7        1.96 ± 57%  perf-profile.calltrace.cycles-pp.ttwu_do_activate.sched_ttwu_pending.__flush_smp_call_function_queue.flush_smp_call_function_queue.do_idle
      2.73            -1.4        1.33 ±  4%      -0.7        2.01 ± 57%  perf-profile.calltrace.cycles-pp.arch_exit_to_user_mode_prepare.do_syscall_64.entry_SYSCALL_64_after_hwframe
      3.50            -1.3        2.17            -0.9        2.62 ± 57%  perf-profile.calltrace.cycles-pp.dequeue_entity.dequeue_entities.dequeue_task_fair.try_to_block_task.__schedule
      2.37            -1.3        1.06 ±  2%      -0.6        1.78 ± 57%  perf-profile.calltrace.cycles-pp.exit_to_user_mode_loop.do_syscall_64.entry_SYSCALL_64_after_hwframe
      1.29            -1.3        0.00            -0.4        0.94 ± 57%  perf-profile.calltrace.cycles-pp._raw_spin_lock.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64.entry_SYSCALL_64_after_hwframe
      1.23            -1.2        0.00            -0.3        0.90 ± 57%  perf-profile.calltrace.cycles-pp.__pick_next_task.__schedule.schedule_idle.do_idle.cpu_startup_entry
      1.16            -1.2        0.00            -0.3        0.86 ± 57%  perf-profile.calltrace.cycles-pp.ttwu_queue_wakelist.try_to_wake_up.wake_up_q.do_mq_timedsend.__x64_sys_mq_timedsend
      1.15            -1.2        0.00            -0.6        0.59 ± 67%  perf-profile.calltrace.cycles-pp.task_h_load.wake_affine.select_task_rq_fair.select_task_rq.try_to_wake_up
      1.15            -1.1        0.00            -0.3        0.84 ± 57%  perf-profile.calltrace.cycles-pp.pick_next_task_fair.__pick_next_task.__schedule.schedule_idle.do_idle
      4.10            -1.1        3.03            -1.0        3.07 ± 57%  perf-profile.calltrace.cycles-pp.__pick_next_task.__schedule.schedule.schedule_hrtimeout_range_clock.wq_sleep
      2.20            -1.1        1.13 ±  5%      -0.6        1.62 ± 57%  perf-profile.calltrace.cycles-pp.enqueue_task.ttwu_do_activate.sched_ttwu_pending.__flush_smp_call_function_queue.flush_smp_call_function_queue
      2.20            -1.1        1.15 ±  4%      -0.6        1.62 ± 57%  perf-profile.calltrace.cycles-pp.switch_fpu_return.arch_exit_to_user_mode_prepare.do_syscall_64.entry_SYSCALL_64_after_hwframe
      1.20            -1.0        0.17 ±141%      -0.3        0.89 ± 57%  perf-profile.calltrace.cycles-pp.do_perf_trace_sched_wakeup_template.perf_trace_sched_wakeup_template.try_to_wake_up.wake_up_q.do_mq_timedsend
      1.74            -1.0        0.73 ±  3%      -0.4        1.34 ± 57%  perf-profile.calltrace.cycles-pp.msg_get.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
      1.32            -1.0        0.37 ± 70%      -0.3        0.98 ± 57%  perf-profile.calltrace.cycles-pp.do_perf_trace_sched_wakeup_template.perf_trace_sched_wakeup_template.try_to_wake_up.wake_up_q.do_mq_timedreceive
      1.93            -0.9        0.99 ±  4%      -0.5        1.42 ± 57%  perf-profile.calltrace.cycles-pp.enqueue_task_fair.enqueue_task.ttwu_do_activate.sched_ttwu_pending.__flush_smp_call_function_queue
      0.93            -0.9        0.00            -0.3        0.67 ± 57%  perf-profile.calltrace.cycles-pp.__check_object_size.store_msg.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64
      0.88            -0.9        0.00            -0.2        0.64 ± 57%  perf-profile.calltrace.cycles-pp.llist_reverse_order.__flush_smp_call_function_queue.flush_smp_call_function_queue.do_idle.cpu_startup_entry
      1.34            -0.8        0.54 ±  5%      -0.3        1.00 ± 57%  perf-profile.calltrace.cycles-pp.perf_trace_sched_wakeup_template.try_to_wake_up.wake_up_q.do_mq_timedreceive.__x64_sys_mq_timedreceive
      1.52            -0.8        0.73 ±  4%      -0.4        1.12 ± 57%  perf-profile.calltrace.cycles-pp.enqueue_entity.enqueue_task_fair.enqueue_task.ttwu_do_activate.sched_ttwu_pending
      0.74            -0.7        0.00            -0.2        0.55 ± 57%  perf-profile.calltrace.cycles-pp.__smp_call_single_queue.ttwu_queue_wakelist.try_to_wake_up.wake_up_q.do_mq_timedsend
      1.05            -0.7        0.34 ± 70%      -0.3        0.78 ± 57%  perf-profile.calltrace.cycles-pp.__switch_to
      1.57            -0.7        0.90 ±  6%      -0.4        1.17 ± 57%  perf-profile.calltrace.cycles-pp.restore_fpregs_from_fpstate.switch_fpu_return.arch_exit_to_user_mode_prepare.do_syscall_64.entry_SYSCALL_64_after_hwframe
      0.67            -0.7        0.00            -0.2        0.46 ± 57%  perf-profile.calltrace.cycles-pp.__wake_up.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
      0.66            -0.7        0.00            -0.2        0.49 ± 57%  perf-profile.calltrace.cycles-pp.update_load_avg.enqueue_entity.enqueue_task_fair.enqueue_task.ttwu_do_activate
      0.60            -0.6        0.00            -0.2        0.42 ± 57%  perf-profile.calltrace.cycles-pp._raw_spin_lock_irqsave.__wake_up.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64
      0.57            -0.6        0.00            -0.2        0.42 ± 57%  perf-profile.calltrace.cycles-pp.set_task_cpu.try_to_wake_up.wake_up_q.do_mq_timedsend.__x64_sys_mq_timedsend
      2.56            -0.3        2.28            -0.6        1.91 ± 57%  perf-profile.calltrace.cycles-pp.pick_next_task_fair.__pick_next_task.__schedule.schedule.schedule_hrtimeout_range_clock
      5.87            +0.9        6.78            -1.5        4.41 ± 57%  perf-profile.calltrace.cycles-pp.intel_idle.cpuidle_enter_state.cpuidle_enter.cpuidle_idle_call.do_idle
      0.00            +0.9        0.91 ± 30%      +0.0        0.00        perf-profile.calltrace.cycles-pp.sched_balance_newidle.pick_next_task_fair.__pick_next_task.__schedule.schedule
     35.80            +3.5       39.29            -8.5       27.26 ± 57%  perf-profile.calltrace.cycles-pp.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
     35.59            +3.6       39.21            -8.5       27.09 ± 57%  perf-profile.calltrace.cycles-pp.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
      0.00            +4.4        4.37 ±  3%      +0.0        0.00        perf-profile.calltrace.cycles-pp.drain_obj_stock.__refill_obj_stock.__memcg_slab_post_alloc_hook.__kmalloc_node_noprof.load_msg
      0.00            +4.4        4.39 ±  4%      +0.0        0.00        perf-profile.calltrace.cycles-pp.drain_obj_stock.__refill_obj_stock.__memcg_slab_free_hook.kfree.free_msg
      0.00            +8.0        8.01 ±  4%      +0.0        0.00        perf-profile.calltrace.cycles-pp.__refill_obj_stock.__memcg_slab_post_alloc_hook.__kmalloc_node_noprof.load_msg.do_mq_timedsend
      0.00            +8.3        8.29 ±  4%      +0.0        0.00        perf-profile.calltrace.cycles-pp.__refill_obj_stock.__memcg_slab_free_hook.kfree.free_msg.do_mq_timedreceive
     28.51           +13.5       41.99            -7.2       21.32 ± 57%  perf-profile.calltrace.cycles-pp.__x64_sys_mq_timedsend.do_syscall_64.entry_SYSCALL_64_after_hwframe
     28.23           +13.5       41.71            -7.1       21.11 ± 57%  perf-profile.calltrace.cycles-pp.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64.entry_SYSCALL_64_after_hwframe
     70.69           +13.7       84.35           -17.4       53.34 ± 57%  perf-profile.calltrace.cycles-pp.entry_SYSCALL_64_after_hwframe
     70.44           +13.8       84.26           -17.3       53.14 ± 57%  perf-profile.calltrace.cycles-pp.do_syscall_64.entry_SYSCALL_64_after_hwframe
      2.99           +20.2       23.24 ±  2%      -0.3        2.70 ± 57%  perf-profile.calltrace.cycles-pp.free_msg.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64.entry_SYSCALL_64_after_hwframe
      2.79           +20.4       23.15 ±  2%      -0.2        2.56 ± 57%  perf-profile.calltrace.cycles-pp.kfree.free_msg.do_mq_timedreceive.__x64_sys_mq_timedreceive.do_syscall_64
      2.43           +20.6       22.98 ±  2%      -0.1        2.28 ± 57%  perf-profile.calltrace.cycles-pp.__memcg_slab_free_hook.kfree.free_msg.do_mq_timedreceive.__x64_sys_mq_timedreceive
      2.26           +26.0       28.23 ±  2%      -0.2        2.03 ± 57%  perf-profile.calltrace.cycles-pp.load_msg.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64.entry_SYSCALL_64_after_hwframe
      0.99           +26.8       27.80 ±  2%      +0.1        1.09 ± 57%  perf-profile.calltrace.cycles-pp.__kmalloc_node_noprof.load_msg.do_mq_timedsend.__x64_sys_mq_timedsend.do_syscall_64
      0.65           +27.0       27.62 ±  2%      +0.2        0.84 ± 57%  perf-profile.calltrace.cycles-pp.__memcg_slab_post_alloc_hook.__kmalloc_node_noprof.load_msg.do_mq_timedsend.__x64_sys_mq_timedsend
     24.25           -12.2       12.02 ±  4%      -6.2       18.00 ± 57%  perf-profile.children.cycles-pp.wake_up_q
     24.00           -12.1       11.93 ±  4%      -6.2       17.82 ± 57%  perf-profile.children.cycles-pp.try_to_wake_up
     23.75           -10.9       12.88            -6.3       17.48 ± 57%  perf-profile.children.cycles-pp.common_startup_64
     23.75           -10.9       12.88            -6.3       17.48 ± 57%  perf-profile.children.cycles-pp.cpu_startup_entry
     23.68           -10.8       12.85            -6.3       17.43 ± 57%  perf-profile.children.cycles-pp.do_idle
     23.65           -10.8       12.82            -6.2       17.41 ± 57%  perf-profile.children.cycles-pp.start_secondary
     19.65            -8.3       11.33            -5.0       14.66 ± 57%  perf-profile.children.cycles-pp.__schedule
     17.14            -6.9       10.28            -4.3       12.80 ± 57%  perf-profile.children.cycles-pp.wq_sleep
     16.24            -6.4        9.82            -4.1       12.13 ± 57%  perf-profile.children.cycles-pp.schedule
     15.92            -6.2        9.69            -4.0       11.88 ± 57%  perf-profile.children.cycles-pp.schedule_hrtimeout_range_clock
     12.46            -6.1        6.32 ±  4%      -3.3        9.20 ± 57%  perf-profile.children.cycles-pp.select_task_rq
     12.19            -6.0        6.17 ±  4%      -3.2        9.00 ± 57%  perf-profile.children.cycles-pp.select_task_rq_fair
      9.91            -4.8        5.09 ±  4%      -2.6        7.33 ± 57%  perf-profile.children.cycles-pp.select_idle_sibling
      4.57            -4.6        0.02 ±141%      -1.3        3.24 ± 57%  perf-profile.children.cycles-pp.poll_idle
      9.27            -4.5        4.80 ±  4%      -2.4        6.85 ± 57%  perf-profile.children.cycles-pp.select_idle_cpu
     12.49            -4.5        8.03            -3.3        9.15 ± 57%  perf-profile.children.cycles-pp.cpuidle_idle_call
     11.41            -4.0        7.39            -3.1        8.36 ± 57%  perf-profile.children.cycles-pp.cpuidle_enter_state
     11.44            -4.0        7.43            -3.1        8.38 ± 57%  perf-profile.children.cycles-pp.cpuidle_enter
      8.17            -4.0        4.18 ±  4%      -2.1        6.04 ± 57%  perf-profile.children.cycles-pp.select_idle_core
      5.76            -3.5        2.28 ±  4%      -1.5        4.25 ± 57%  perf-profile.children.cycles-pp.flush_smp_call_function_queue
      5.47            -3.1        2.35 ±  4%      -1.4        4.04 ± 57%  perf-profile.children.cycles-pp.__flush_smp_call_function_queue
      4.30            -2.4        1.94 ±  4%      -1.1        3.18 ± 57%  perf-profile.children.cycles-pp.sched_ttwu_pending
      4.08            -2.2        1.90            -1.1        3.02 ± 57%  perf-profile.children.cycles-pp.schedule_idle
      3.47            -2.2        1.30 ±  2%      -0.9        2.57 ± 57%  perf-profile.children.cycles-pp.store_msg
      5.87            -2.1        3.78            -1.5        4.38 ± 57%  perf-profile.children.cycles-pp.__pick_next_task
      4.84            -2.0        2.86            -1.2        3.63 ± 57%  perf-profile.children.cycles-pp.try_to_block_task
      4.73            -1.9        2.79            -1.2        3.54 ± 57%  perf-profile.children.cycles-pp.dequeue_entities
      4.73            -1.9        2.82            -1.2        3.55 ± 57%  perf-profile.children.cycles-pp.dequeue_task_fair
      4.04            -1.8        2.26            -1.1        2.98 ± 57%  perf-profile.children.cycles-pp._raw_spin_lock
      3.72            -1.7        2.03 ±  4%      -0.9        2.77 ± 57%  perf-profile.children.cycles-pp.enqueue_task
      2.40            -1.6        0.85 ±  3%      -0.6        1.79 ± 57%  perf-profile.children.cycles-pp._copy_to_user
      3.39            -1.5        1.86 ±  3%      -0.9        2.52 ± 57%  perf-profile.children.cycles-pp.ttwu_do_activate
      2.56            -1.5        1.04 ±  5%      -0.7        1.90 ± 57%  perf-profile.children.cycles-pp.perf_trace_sched_wakeup_template
      2.54            -1.5        1.03 ±  5%      -0.7        1.88 ± 57%  perf-profile.children.cycles-pp.do_perf_trace_sched_wakeup_template
      2.76            -1.4        1.34 ±  4%      -0.7        2.03 ± 57%  perf-profile.children.cycles-pp.arch_exit_to_user_mode_prepare
      3.75            -1.4        2.34            -0.9        2.81 ± 57%  perf-profile.children.cycles-pp.dequeue_entity
      2.32            -1.4        0.95 ±  4%      -0.6        1.72 ± 57%  perf-profile.children.cycles-pp.ttwu_queue_wakelist
      2.72            -1.3        1.38 ±  2%      -0.7        2.05 ± 57%  perf-profile.children.cycles-pp.update_curr
      2.38            -1.3        1.06 ±  2%      -0.6        1.79 ± 57%  perf-profile.children.cycles-pp.exit_to_user_mode_loop
      4.24            -1.2        2.99            -1.1        3.16 ± 57%  perf-profile.children.cycles-pp.pick_next_task_fair
      2.21            -1.1        1.15 ±  5%      -0.6        1.63 ± 57%  perf-profile.children.cycles-pp.switch_fpu_return
      1.76            -1.0        0.73 ±  3%      -0.4        1.37 ± 57%  perf-profile.children.cycles-pp.msg_get
      1.67            -1.0        0.65 ±  3%      -0.6        1.10 ± 57%  perf-profile.children.cycles-pp._raw_spin_lock_irqsave
      2.47            -1.0        1.47 ±  3%      -0.6        1.84 ± 57%  perf-profile.children.cycles-pp.enqueue_task_fair
      2.39            -1.0        1.42            -0.6        1.78 ± 57%  perf-profile.children.cycles-pp.raw_spin_rq_lock_nested
      2.29            -1.0        1.32            -0.6        1.69 ± 57%  perf-profile.children.cycles-pp.update_load_avg
      1.60            -1.0        0.64            -0.4        1.20 ± 57%  perf-profile.children.cycles-pp.switch_mm_irqs_off
      1.51            -0.9        0.57 ±  5%      -0.4        1.12 ± 57%  perf-profile.children.cycles-pp.__smp_call_single_queue
      1.84            -0.9        0.93 ±  6%      -0.5        1.35 ± 57%  perf-profile.children.cycles-pp.wake_affine
      1.49            -0.9        0.59            -0.4        1.10 ± 57%  perf-profile.children.cycles-pp.__check_object_size
      1.61            -0.9        0.72 ±  5%      -0.4        1.21 ± 57%  perf-profile.children.cycles-pp.update_rq_clock_task
      1.73            -0.9        0.87 ±  2%      -0.4        1.30 ± 57%  perf-profile.children.cycles-pp.update_se
      1.51            -0.9        0.65 ±  2%      -0.4        1.14 ± 57%  perf-profile.children.cycles-pp.wakeup_preempt
      2.00            -0.8        1.15 ±  3%      -0.5        1.48 ± 57%  perf-profile.children.cycles-pp.enqueue_entity
      1.26            -0.8        0.42 ±  3%      -0.5        0.80 ± 57%  perf-profile.children.cycles-pp.__wake_up
      1.31            -0.7        0.58 ±  5%      -0.3        0.97 ± 57%  perf-profile.children.cycles-pp.set_task_cpu
      1.15            -0.7        0.45 ±  3%      -0.3        0.85 ± 57%  perf-profile.children.cycles-pp.msg_insert
      1.04            -0.7        0.35 ±  5%      -0.3        0.78 ± 57%  perf-profile.children.cycles-pp.perf_trace_buf_alloc
      1.57            -0.7        0.90 ±  5%      -0.4        1.17 ± 57%  perf-profile.children.cycles-pp.restore_fpregs_from_fpstate
      1.00            -0.7        0.34 ±  7%      -0.3        0.74 ± 57%  perf-profile.children.cycles-pp.perf_swevent_get_recursion_context
      0.95            -0.7        0.29 ±  4%      -0.3        0.70 ± 57%  perf-profile.children.cycles-pp.llist_reverse_order
      1.14            -0.6        0.50 ±  5%      -0.3        0.84 ± 57%  perf-profile.children.cycles-pp.migrate_task_rq_fair
      1.11            -0.6        0.51            -0.3        0.82 ± 57%  perf-profile.children.cycles-pp.set_next_entity
      0.95            -0.6        0.39 ±  2%      -0.2        0.71 ± 57%  perf-profile.children.cycles-pp.__update_idle_core
      1.04            -0.6        0.49 ±  2%      -0.3        0.77 ± 57%  perf-profile.children.cycles-pp.pick_task_fair
      1.15            -0.6        0.60 ±  7%      -0.3        0.84 ± 57%  perf-profile.children.cycles-pp.task_h_load
      1.06            -0.5        0.52            -0.3        0.79 ± 57%  perf-profile.children.cycles-pp.set_next_task_idle
      0.84            -0.5        0.30 ±  5%      -0.2        0.63 ± 57%  perf-profile.children.cycles-pp.call_function_single_prep_ipi
      1.04            -0.5        0.51            -0.3        0.76 ± 57%  perf-profile.children.cycles-pp._find_next_bit
      1.38            -0.5        0.85            -0.4        1.02 ± 57%  perf-profile.children.cycles-pp.update_cfs_rq_load_avg
      1.28            -0.5        0.75            -0.3        0.95 ± 57%  perf-profile.children.cycles-pp.__switch_to
      0.85            -0.5        0.34 ±  3%      -0.2        0.66 ± 57%  perf-profile.children.cycles-pp.cpuacct_charge
      0.77 ±  4%      -0.5        0.25            -0.2        0.55 ± 57%  perf-profile.children.cycles-pp.__bitmap_andnot
      0.88            -0.5        0.41 ±  6%      -0.2        0.66 ± 57%  perf-profile.children.cycles-pp.update_entity_lag
      0.94            -0.5        0.48            -0.2        0.69 ± 57%  perf-profile.children.cycles-pp.prepare_task_switch
      0.74            -0.4        0.31 ±  2%      -0.2        0.56 ± 57%  perf-profile.children.cycles-pp.check_heap_object
      0.75            -0.4        0.32 ±  5%      -0.2        0.56 ± 57%  perf-profile.children.cycles-pp.requeue_delayed_entity
      0.80            -0.4        0.40            -0.2        0.60 ± 57%  perf-profile.children.cycles-pp.wakeup_preempt_fair
      0.55            -0.4        0.16 ±  2%      -0.1        0.40 ± 57%  perf-profile.children.cycles-pp.native_sched_clock
      0.57            -0.4        0.18 ±  4%      -0.2        0.41 ± 57%  perf-profile.children.cycles-pp.entry_SYSRETQ_unsafe_stack
      0.55            -0.4        0.17            -0.1        0.41 ± 57%  perf-profile.children.cycles-pp.os_xsave
      0.58            -0.4        0.20            -0.2        0.43 ± 57%  perf-profile.children.cycles-pp.sched_clock_cpu
      1.39            -0.4        1.01            -0.4        1.03 ± 57%  perf-profile.children.cycles-pp.native_queued_spin_lock_slowpath
      1.18            -0.4        0.81            -0.3        0.88 ± 57%  perf-profile.children.cycles-pp.___task_rq_lock
      0.51 ±  3%      -0.4        0.14 ±  3%      -0.2        0.36 ± 58%  perf-profile.children.cycles-pp._copy_from_user
      0.56            -0.4        0.19 ±  2%      -0.1        0.41 ± 57%  perf-profile.children.cycles-pp.update_rq_clock
      0.51            -0.3        0.17 ±  2%      -0.1        0.38 ± 57%  perf-profile.children.cycles-pp.sched_clock
      0.56            -0.3        0.23 ±  3%      -0.1        0.44 ± 57%  perf-profile.children.cycles-pp.simple_inode_init_ts
      0.89            -0.3        0.56            -0.2        0.67 ± 57%  perf-profile.children.cycles-pp.put_prev_entity
      0.53 ±  3%      -0.3        0.21 ±  2%      -0.2        0.38 ± 57%  perf-profile.children.cycles-pp.__put_user_4
      0.68 ± 12%      -0.3        0.36 ±  4%      -0.1        0.57 ± 57%  perf-profile.children.cycles-pp.stress_switch_mq
      0.52            -0.3        0.20            -0.1        0.37 ± 57%  perf-profile.children.cycles-pp.set_next_task_fair
      0.55            -0.3        0.24 ±  5%      -0.1        0.40 ± 57%  perf-profile.children.cycles-pp.__switch_to_asm
      0.51            -0.3        0.21 ±  3%      -0.1        0.40 ± 57%  perf-profile.children.cycles-pp.inode_set_ctime_current
      0.56            -0.3        0.26 ±  3%      -0.1        0.42 ± 57%  perf-profile.children.cycles-pp.fdget
      0.52            -0.3        0.23 ±  3%      -0.1        0.39 ± 57%  perf-profile.children.cycles-pp.mm_cid_switch_to
      0.54            -0.3        0.25 ±  3%      -0.1        0.40 ± 57%  perf-profile.children.cycles-pp.remove_entity_load_avg
      0.79            -0.3        0.51            -0.2        0.59 ± 57%  perf-profile.children.cycles-pp.asm_sysvec_call_function_single
      0.37            -0.3        0.11 ±  7%      -0.1        0.27 ± 57%  perf-profile.children.cycles-pp.__resched_curr
      0.57            -0.2        0.32            -0.1        0.42 ± 57%  perf-profile.children.cycles-pp.__update_load_avg_cfs_rq
      0.35            -0.2        0.12 ±  4%      -0.1        0.26 ± 57%  perf-profile.children.cycles-pp.avg_vruntime
      0.62            -0.2        0.39 ±  2%      -0.2        0.47 ± 57%  perf-profile.children.cycles-pp.sysvec_call_function_single
      0.34            -0.2        0.11            -0.1        0.25 ± 57%  perf-profile.children.cycles-pp.entry_SYSCALL_64
      0.58            -0.2        0.36            -0.1        0.44 ± 57%  perf-profile.children.cycles-pp.__enqueue_entity
      0.52            -0.2        0.30 ±  3%      -0.1        0.38 ± 57%  perf-profile.children.cycles-pp.perf_tp_event
      0.46            -0.2        0.24            -0.1        0.34 ± 57%  perf-profile.children.cycles-pp.__pick_eevdf
      0.35            -0.2        0.14 ±  3%      -0.1        0.26 ± 57%  perf-profile.children.cycles-pp.__wrgsbase_inactive
      0.56            -0.2        0.36            -0.1        0.42 ± 57%  perf-profile.children.cycles-pp.__sysvec_call_function_single
      0.33            -0.2        0.13 ±  3%      -0.1        0.24 ± 57%  perf-profile.children.cycles-pp.__update_load_avg_se
      0.31 ±  2%      -0.2        0.12 ±  4%      -0.1        0.24 ± 57%  perf-profile.children.cycles-pp.__virt_addr_valid
      0.58            -0.2        0.40            -0.1        0.44 ± 57%  perf-profile.children.cycles-pp.menu_select
      0.32 ±  5%      -0.2        0.13            -0.1        0.22 ± 57%  perf-profile.children.cycles-pp.__check_heap_object
      0.31            -0.2        0.13 ±  3%      -0.1        0.23 ± 57%  perf-profile.children.cycles-pp.place_entity
      0.32            -0.2        0.14            -0.1        0.24 ± 57%  perf-profile.children.cycles-pp.tick_nohz_idle_enter
      0.36            -0.2        0.18 ±  2%      -0.1        0.27 ± 57%  perf-profile.children.cycles-pp.perf_trace_sched_stat_runtime
      0.29            -0.2        0.11            -0.1        0.22 ± 59%  perf-profile.children.cycles-pp.syscall_return_via_sysret
      0.33            -0.2        0.16 ±  3%      -0.1        0.24 ± 57%  perf-profile.children.cycles-pp.do_perf_trace_sched_stat_runtime
      0.35            -0.2        0.18 ±  2%      -0.1        0.26 ± 57%  perf-profile.children.cycles-pp.ktime_get
      0.25            -0.2        0.08 ±  5%      -0.1        0.18 ± 57%  perf-profile.children.cycles-pp.entry_SYSCALL_64_safe_stack
      0.37            -0.2        0.20 ±  4%      -0.1        0.27 ± 57%  perf-profile.children.cycles-pp.___perf_sw_event
      0.23            -0.2        0.07 ±  6%      -0.1        0.17 ± 57%  perf-profile.children.cycles-pp.tick_nohz_idle_exit
      0.22            -0.2        0.07            -0.1        0.16 ± 57%  perf-profile.children.cycles-pp.read_tsc
      0.21            -0.1        0.06 ±  7%      -0.1        0.16 ± 57%  perf-profile.children.cycles-pp.__rdgsbase_inactive
      0.25            -0.1        0.11 ±  7%      -0.1        0.18 ± 57%  perf-profile.children.cycles-pp.strnlen
      0.16            -0.1        0.02 ±141%      -0.0        0.11 ± 57%  perf-profile.children.cycles-pp.ct_idle_exit
      0.32            -0.1        0.18            -0.1        0.24 ± 57%  perf-profile.children.cycles-pp.__dequeue_entity
      0.23 ±  2%      -0.1        0.10 ±  4%      -0.1        0.18 ± 57%  perf-profile.children.cycles-pp.check_stack_object
      0.53            -0.1        0.40 ±  2%      -0.1        0.40 ± 57%  perf-profile.children.cycles-pp.sysvec_apic_timer_interrupt
      0.23 ±  2%      -0.1        0.09 ±  5%      -0.1        0.17 ± 57%  perf-profile.children.cycles-pp._raw_spin_unlock_irqrestore
      0.30 ±  3%      -0.1        0.17 ±  4%      -0.1        0.23 ± 57%  perf-profile.children.cycles-pp.tick_nohz_handler
      0.20            -0.1        0.07            +0.3        0.46 ± 57%  perf-profile.children.cycles-pp.__account_obj_stock
      0.13            -0.1        0.00            -0.0        0.09 ± 57%  perf-profile.children.cycles-pp.ct_kernel_exit_state
      0.41            -0.1        0.28 ±  2%      -0.1        0.30 ± 57%  perf-profile.children.cycles-pp.hrtimer_interrupt
      0.41 ±  2%      -0.1        0.29 ±  3%      -0.1        0.31 ± 57%  perf-profile.children.cycles-pp.__sysvec_apic_timer_interrupt
      0.31 ±  2%      -0.1        0.18 ±  2%      -0.1        0.24 ± 57%  perf-profile.children.cycles-pp.__hrtimer_run_queues
      0.28 ±  2%      -0.1        0.16 ±  6%      -0.1        0.21 ± 57%  perf-profile.children.cycles-pp.update_process_times
      0.33            -0.1        0.21 ±  6%      -0.1        0.25 ± 57%  perf-profile.children.cycles-pp.attach_entity_load_avg
      0.12 ±  3%      -0.1        0.00            -0.1        0.05 ± 58%  perf-profile.children.cycles-pp.__do_notify
      0.19 ±  2%      -0.1        0.07 ±  7%      -0.0        0.14 ± 57%  perf-profile.children.cycles-pp.wake_q_add_safe
      0.23 ±  2%      -0.1        0.11            -0.1        0.17 ± 57%  perf-profile.children.cycles-pp.__kmalloc_cache_noprof
      0.18            -0.1        0.07 ±  6%      -0.1        0.13 ± 57%  perf-profile.children.cycles-pp.nohz_run_idle_balance
      0.56            -0.1        0.46            -0.1        0.42 ± 57%  perf-profile.children.cycles-pp.asm_sysvec_apic_timer_interrupt
      0.15            -0.1        0.05            -0.0        0.11 ± 57%  perf-profile.children.cycles-pp._raw_spin_unlock
      0.17            -0.1        0.08            -0.1        0.12 ± 57%  perf-profile.children.cycles-pp.security_msg_msg_free
      0.15            -0.1        0.06            -0.0        0.12 ± 57%  perf-profile.children.cycles-pp.inode_set_ctime_to_ts
      0.16 ±  2%      -0.1        0.08 ±  5%      -0.0        0.12 ± 57%  perf-profile.children.cycles-pp.dl_server_update
      0.13            -0.1        0.06 ±  8%      -0.0        0.10 ± 57%  perf-profile.children.cycles-pp.timestamp_truncate
      0.15 ±  3%      -0.1        0.08            -0.0        0.12 ± 57%  perf-profile.children.cycles-pp.perf_trace_buf_update
      0.07            -0.1        0.00            -0.0        0.04 ± 57%  perf-profile.children.cycles-pp.kick_ilb
      0.13 ±  3%      -0.1        0.06            -0.0        0.09 ± 57%  perf-profile.children.cycles-pp.ktime_get_coarse_real_ts64_mg
      0.13 ±  3%      -0.1        0.06            -0.0        0.10 ± 57%  perf-profile.children.cycles-pp.migrate_disable_switch
      0.12 ±  6%      -0.1        0.05 ±  8%      -0.0        0.08 ± 58%  perf-profile.children.cycles-pp.__cgroup_account_cputime
      0.11 ±  4%      -0.1        0.05            -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.cpuidle_governor_latency_req
      0.14            -0.1        0.08 ±  5%      -0.0        0.10 ± 57%  perf-profile.children.cycles-pp.update_curr_dl_se
      0.10            -0.1        0.05            -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.ct_kernel_exit
      0.11            -0.1        0.06            -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.tracing_gen_ctx_irq_test
      0.10 ±  4%      -0.0        0.05            -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.__rb_insert_augmented
      0.10            -0.0        0.06 ±  8%      -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.rest_init
      0.10            -0.0        0.06 ±  8%      -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.start_kernel
      0.10            -0.0        0.06 ±  8%      -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.x86_64_start_kernel
      0.10            -0.0        0.06 ±  8%      -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.x86_64_start_reservations
      0.08 ± 10%      -0.0        0.04 ± 71%      -0.0        0.06 ± 59%  perf-profile.children.cycles-pp.mq_timedreceive
      0.15            -0.0        0.11 ±  4%      -0.0        0.11 ± 57%  perf-profile.children.cycles-pp.vruntime_eligible
      0.13            -0.0        0.09            -0.0        0.10 ± 57%  perf-profile.children.cycles-pp.put_prev_task_fair
      0.09            -0.0        0.05 ±  8%      -0.0        0.07 ± 57%  perf-profile.children.cycles-pp.native_irq_return_iret
      0.08            -0.0        0.05            -0.0        0.06 ± 57%  perf-profile.children.cycles-pp.choose_new_asid
      0.13            -0.0        0.11            -0.0        0.10 ± 57%  perf-profile.children.cycles-pp.__irq_exit_rcu
      0.07            -0.0        0.05            -0.0        0.05 ± 57%  perf-profile.children.cycles-pp.__set_next_task_fair
      0.76            -0.0        0.74            -0.2        0.56 ± 57%  perf-profile.children.cycles-pp.finish_task_switch
      0.09            -0.0        0.07 ±  6%      -0.0        0.07 ± 57%  perf-profile.children.cycles-pp.propagate_entity_load_avg
      0.10            -0.0        0.09            -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.handle_softirqs
      0.07            -0.0        0.06            -0.0        0.05 ± 57%  perf-profile.children.cycles-pp.clockevents_program_event
      0.07            +0.0        0.08            -0.0        0.05 ± 57%  perf-profile.children.cycles-pp.perf_swevent_event
      0.48            +0.0        0.49            -0.1        0.36 ± 57%  perf-profile.children.cycles-pp.process_simple
      0.05            +0.0        0.06 ±  7%      -0.0        0.04 ± 57%  perf-profile.children.cycles-pp.sched_update_worker
      0.05            +0.0        0.07            -0.0        0.04 ± 57%  perf-profile.children.cycles-pp.arch_cpu_idle_enter
      0.07 ± 11%      +0.0        0.09 ±  5%      +0.0        0.07 ± 58%  perf-profile.children.cycles-pp.mq_timedsend
      0.15 ±  3%      +0.0        0.20 ±  4%      -0.0        0.12 ± 57%  perf-profile.children.cycles-pp.x64_sys_call
      0.00            +0.1        0.05            +0.0        0.00        perf-profile.children.cycles-pp.__sched_balance_update_blocked_averages
      0.00            +0.1        0.05            +0.0        0.00        perf-profile.children.cycles-pp.update_cfs_group
      0.00            +0.1        0.06 ± 23%      +0.0        0.00        perf-profile.children.cycles-pp.generic_perform_write
      0.00            +0.1        0.06 ±  7%      +0.0        0.00        perf-profile.children.cycles-pp.detach_tasks
      0.00            +0.1        0.06 ± 29%      +0.0        0.00        perf-profile.children.cycles-pp.shmem_file_write_iter
      0.00            +0.1        0.06 ± 29%      +0.0        0.00        perf-profile.children.cycles-pp.vfs_write
      0.00            +0.1        0.07 ± 25%      +0.0        0.00        perf-profile.children.cycles-pp.ksys_write
      0.00            +0.1        0.08 ± 30%      +0.0        0.00        perf-profile.children.cycles-pp.record__pushfn
      0.04 ± 71%      +0.1        0.12 ± 35%      -0.0        0.01 ±173%  perf-profile.children.cycles-pp.perf_mmap__push
      0.54 ±  2%      +0.1        0.62 ±  7%      -0.1        0.40 ± 57%  perf-profile.children.cycles-pp.cmd_record
      0.04 ± 71%      +0.1        0.13 ± 35%      -0.0        0.04 ± 58%  perf-profile.children.cycles-pp.handle_internal_command
      0.04 ± 71%      +0.1        0.13 ± 35%      -0.0        0.04 ± 58%  perf-profile.children.cycles-pp.main
      0.04 ± 71%      +0.1        0.13 ± 35%      -0.0        0.04 ± 58%  perf-profile.children.cycles-pp.run_builtin
      0.04 ± 70%      +0.1        0.13 ± 32%      -0.0        0.04 ± 57%  perf-profile.children.cycles-pp.record__mmap_read_evlist
      0.10 ±  4%      +0.1        0.20 ±  4%      -0.0        0.08 ± 57%  perf-profile.children.cycles-pp.do_perf_trace_sched_switch
      0.00            +0.1        0.10 ±  4%      +0.0        0.00        perf-profile.children.cycles-pp.ct_idle_enter
      0.13 ±  3%      +0.2        0.29 ±  4%      -0.0        0.10 ± 57%  perf-profile.children.cycles-pp.perf_trace_sched_switch
      0.21 ±  6%      +0.6        0.78 ±  3%      -0.0        0.16 ± 57%  perf-profile.children.cycles-pp.update_sg_lb_stats
      0.22 ±  6%      +0.6        0.81 ±  4%      -0.1        0.17 ± 57%  perf-profile.children.cycles-pp.update_sd_lb_stats
      0.22 ±  6%      +0.6        0.81 ±  3%      -0.1        0.17 ± 57%  perf-profile.children.cycles-pp.sched_balance_find_src_group
      0.40 ±  3%      +0.7        1.08 ±  4%      -0.1        0.30 ± 57%  perf-profile.children.cycles-pp.sched_balance_newidle
      0.27 ±  6%      +0.7        0.97 ±  4%      -0.1        0.21 ± 57%  perf-profile.children.cycles-pp.sched_balance_rq
      5.90            +0.9        6.81            -1.5        4.43 ± 57%  perf-profile.children.cycles-pp.intel_idle
     35.82            +3.5       39.30            -8.6       27.27 ± 57%  perf-profile.children.cycles-pp.__x64_sys_mq_timedreceive
     35.70            +3.5       39.25            -8.5       27.18 ± 57%  perf-profile.children.cycles-pp.do_mq_timedreceive
      0.00            +8.8        8.78 ±  3%      +0.0        0.00        perf-profile.children.cycles-pp.drain_obj_stock
     28.34           +13.4       41.76            -7.1       21.20 ± 57%  perf-profile.children.cycles-pp.do_mq_timedsend
     28.52           +13.5       41.99            -7.2       21.34 ± 57%  perf-profile.children.cycles-pp.__x64_sys_mq_timedsend
     70.76           +13.7       84.45           -17.4       53.40 ± 57%  perf-profile.children.cycles-pp.entry_SYSCALL_64_after_hwframe
     70.56           +13.8       84.39           -17.3       53.24 ± 57%  perf-profile.children.cycles-pp.do_syscall_64
      0.08           +16.2       16.31 ±  4%      +0.1        0.15 ± 57%  perf-profile.children.cycles-pp.__refill_obj_stock
      3.22           +20.1       23.33 ±  2%      -0.3        2.91 ± 57%  perf-profile.children.cycles-pp.kfree
      3.01           +20.2       23.25 ±  2%      -0.3        2.72 ± 57%  perf-profile.children.cycles-pp.free_msg
      2.46           +20.5       23.00 ±  2%      -0.1        2.31 ± 57%  perf-profile.children.cycles-pp.__memcg_slab_free_hook
      2.31           +25.9       28.25 ±  2%      -0.2        2.07 ± 57%  perf-profile.children.cycles-pp.load_msg
      1.00           +26.8       27.81 ±  2%      +0.1        1.10 ± 57%  perf-profile.children.cycles-pp.__kmalloc_node_noprof
      0.68           +27.0       27.65 ±  2%      +0.2        0.86 ± 57%  perf-profile.children.cycles-pp.__memcg_slab_post_alloc_hook
      4.41            -4.4        0.02 ±141%      -1.3        3.13 ± 57%  perf-profile.self.cycles-pp.poll_idle
      6.79            -3.2        3.63 ±  5%      -1.8        5.04 ± 57%  perf-profile.self.cycles-pp.select_idle_core
      3.07            -1.7        1.33 ±  4%      -0.7        2.36 ± 57%  perf-profile.self.cycles-pp.do_mq_timedreceive
      2.90            -1.6        1.31 ±  3%      -0.7        2.16 ± 57%  perf-profile.self.cycles-pp.__schedule
      2.37            -1.5        0.84 ±  3%      -0.6        1.77 ± 57%  perf-profile.self.cycles-pp._copy_to_user
      2.73            -1.5        1.22 ±  3%      -0.7        2.00 ± 57%  perf-profile.self.cycles-pp.do_mq_timedsend
      2.63            -1.4        1.25 ±  3%      -0.7        1.93 ± 57%  perf-profile.self.cycles-pp._raw_spin_lock
      1.64            -1.0        0.64 ±  3%      -0.6        1.08 ± 57%  perf-profile.self.cycles-pp._raw_spin_lock_irqsave
      1.46            -0.9        0.55 ±  2%      -0.4        1.10 ± 57%  perf-profile.self.cycles-pp.switch_mm_irqs_off
      1.54            -0.9        0.67 ±  6%      -0.4        1.16 ± 57%  perf-profile.self.cycles-pp.update_rq_clock_task
      1.48            -0.9        0.62 ±  3%      -0.4        1.13 ± 57%  perf-profile.self.cycles-pp.msg_get
      1.36            -0.8        0.58 ±  3%      -0.3        1.02 ± 57%  perf-profile.self.cycles-pp.exit_to_user_mode_loop
      1.11            -0.7        0.44 ±  4%      -0.3        0.82 ± 57%  perf-profile.self.cycles-pp.msg_insert
      1.56            -0.7        0.90 ±  6%      -0.4        1.17 ± 57%  perf-profile.self.cycles-pp.restore_fpregs_from_fpstate
      0.95            -0.7        0.29 ±  5%      -0.3        0.70 ± 57%  perf-profile.self.cycles-pp.llist_reverse_order
      1.00            -0.7        0.34 ±  7%      -0.3        0.74 ± 57%  perf-profile.self.cycles-pp.perf_swevent_get_recursion_context
      1.19            -0.6        0.59 ±  3%      -0.3        0.90 ± 57%  perf-profile.self.cycles-pp.wq_sleep
      0.92            -0.6        0.36 ±  6%      -0.2        0.69 ± 57%  perf-profile.self.cycles-pp.do_perf_trace_sched_wakeup_template
      0.90            -0.6        0.34 ±  2%      -0.2        0.68 ± 57%  perf-profile.self.cycles-pp.__update_idle_core
      1.15            -0.5        0.60 ±  7%      -0.3        0.84 ± 57%  perf-profile.self.cycles-pp.task_h_load
      0.83            -0.5        0.30 ±  5%      -0.2        0.62 ± 57%  perf-profile.self.cycles-pp.call_function_single_prep_ipi
      0.97            -0.5        0.44 ±  2%      -0.2        0.73 ± 57%  perf-profile.self.cycles-pp.dequeue_entities
      0.84            -0.5        0.34 ±  3%      -0.2        0.65 ± 57%  perf-profile.self.cycles-pp.cpuacct_charge
      0.96            -0.5        0.47 ±  4%      -0.2        0.72 ± 57%  perf-profile.self.cycles-pp.do_syscall_64
      1.23            -0.5        0.74            -0.3        0.91 ± 57%  perf-profile.self.cycles-pp.__switch_to
      0.73 ±  4%      -0.5        0.24 ±  3%      -0.2        0.52 ± 57%  perf-profile.self.cycles-pp.__bitmap_andnot
      0.69            -0.5        0.20 ±  4%      -0.2        0.51 ± 57%  perf-profile.self.cycles-pp.flush_smp_call_function_queue
      0.94            -0.5        0.47            -0.2        0.69 ± 57%  perf-profile.self.cycles-pp._find_next_bit
      0.77            -0.4        0.36 ±  6%      -0.2        0.58 ± 57%  perf-profile.self.cycles-pp.update_entity_lag
      0.76            -0.4        0.36 ±  3%      -0.2        0.56 ± 57%  perf-profile.self.cycles-pp.update_load_avg
      0.67            -0.4        0.27 ±  5%      -0.2        0.49 ± 57%  perf-profile.self.cycles-pp.__smp_call_single_queue
      0.70            -0.4        0.31 ±  2%      -0.2        0.52 ± 57%  perf-profile.self.cycles-pp.pick_next_task_fair
      0.55            -0.4        0.17 ±  2%      -0.1        0.41 ± 57%  perf-profile.self.cycles-pp.os_xsave
      0.56            -0.4        0.18 ±  4%      -0.1        0.41 ± 57%  perf-profile.self.cycles-pp.entry_SYSRETQ_unsafe_stack
      0.63            -0.4        0.25            -0.2        0.46 ± 57%  perf-profile.self.cycles-pp.switch_fpu_return
      1.38            -0.4        1.01            -0.4        1.02 ± 57%  perf-profile.self.cycles-pp.native_queued_spin_lock_slowpath
      0.69            -0.4        0.33 ±  5%      -0.2        0.51 ± 57%  perf-profile.self.cycles-pp.wake_affine
      0.53            -0.4        0.16            -0.1        0.38 ± 57%  perf-profile.self.cycles-pp.native_sched_clock
      0.67            -0.4        0.30 ±  2%      -0.1        0.52 ± 57%  perf-profile.self.cycles-pp.kfree
      0.75            -0.4        0.39 ±  3%      -0.2        0.57 ± 57%  perf-profile.self.cycles-pp.update_curr
      0.67            -0.4        0.31 ±  5%      -0.2        0.49 ± 57%  perf-profile.self.cycles-pp.ttwu_queue_wakelist
      0.53            -0.4        0.18 ±  2%      -0.1        0.38 ± 57%  perf-profile.self.cycles-pp.arch_exit_to_user_mode_prepare
      0.49 ±  2%      -0.4        0.14 ±  3%      -0.1        0.35 ± 58%  perf-profile.self.cycles-pp._copy_from_user
      0.63            -0.3        0.28 ±  3%      -0.2        0.47 ± 57%  perf-profile.self.cycles-pp.schedule_hrtimeout_range_clock
      0.58            -0.3        0.23 ±  4%      -0.1        0.43 ± 57%  perf-profile.self.cycles-pp.select_idle_sibling
      0.58            -0.3        0.24 ±  7%      -0.2        0.42 ± 57%  perf-profile.self.cycles-pp.migrate_task_rq_fair
      0.64            -0.3        0.31            -0.2        0.47 ± 57%  perf-profile.self.cycles-pp.prepare_task_switch
      0.82            -0.3        0.49 ±  3%      -0.2        0.62 ± 57%  perf-profile.self.cycles-pp.select_idle_cpu
      0.52 ±  3%      -0.3        0.21 ±  2%      -0.1        0.37 ± 57%  perf-profile.self.cycles-pp.__put_user_4
      0.63 ± 11%      -0.3        0.32 ±  5%      -0.1        0.52 ± 57%  perf-profile.self.cycles-pp.stress_switch_mq
      0.54            -0.3        0.24 ±  5%      -0.1        0.40 ± 57%  perf-profile.self.cycles-pp.__switch_to_asm
      0.54            -0.3        0.25 ±  3%      -0.1        0.40 ± 57%  perf-profile.self.cycles-pp.fdget
      0.51            -0.3        0.22 ±  4%      -0.1        0.38 ± 57%  perf-profile.self.cycles-pp.mm_cid_switch_to
      0.44            -0.3        0.15 ±  6%      -0.1        0.33 ± 57%  perf-profile.self.cycles-pp.select_task_rq_fair
      0.43            -0.3        0.15 ±  5%      -0.1        0.32 ± 57%  perf-profile.self.cycles-pp.sched_ttwu_pending
      0.49            -0.3        0.23 ±  6%      -0.1        0.37 ± 57%  perf-profile.self.cycles-pp.enqueue_task
      0.59            -0.2        0.34 ±  2%      -0.1        0.44 ± 57%  perf-profile.self.cycles-pp.try_to_wake_up
      0.73            -0.2        0.48            -0.2        0.54 ± 57%  perf-profile.self.cycles-pp.update_cfs_rq_load_avg
      0.35            -0.2        0.11 ±  4%      -0.1        0.26 ± 57%  perf-profile.self.cycles-pp.__resched_curr
      0.40            -0.2        0.16 ±  2%      -0.1        0.30 ± 57%  perf-profile.self.cycles-pp.__pick_next_task
      0.36            -0.2        0.12 ±  3%      -0.1        0.26 ± 57%  perf-profile.self.cycles-pp.cpuidle_idle_call
      0.34            -0.2        0.11            -0.1        0.25 ± 57%  perf-profile.self.cycles-pp.entry_SYSCALL_64
      0.57            -0.2        0.36            -0.1        0.43 ± 57%  perf-profile.self.cycles-pp.__enqueue_entity
      0.51            -0.2        0.31            -0.1        0.38 ± 57%  perf-profile.self.cycles-pp.__update_load_avg_cfs_rq
      0.34            -0.2        0.14 ±  5%      -0.1        0.26 ± 57%  perf-profile.self.cycles-pp.wakeup_preempt
      0.34            -0.2        0.14 ±  3%      -0.1        0.25 ± 57%  perf-profile.self.cycles-pp.__wrgsbase_inactive
      0.39            -0.2        0.20 ±  2%      -0.1        0.29 ± 57%  perf-profile.self.cycles-pp.do_idle
      0.31 ±  5%      -0.2        0.11            -0.1        0.21 ± 58%  perf-profile.self.cycles-pp.__check_heap_object
      0.37            -0.2        0.17 ±  2%      -0.1        0.27 ± 57%  perf-profile.self.cycles-pp.check_heap_object
      0.51            -0.2        0.32            -0.1        0.38 ± 57%  perf-profile.self.cycles-pp.schedule
      0.36            -0.2        0.18 ±  2%      -0.1        0.27 ± 57%  perf-profile.self.cycles-pp.__pick_eevdf
      0.61            -0.2        0.43            -0.2        0.46 ± 57%  perf-profile.self.cycles-pp.dequeue_entity
      0.29 ±  2%      -0.2        0.11 ±  4%      -0.1        0.22 ± 57%  perf-profile.self.cycles-pp.__virt_addr_valid
      0.30            -0.2        0.12            -0.1        0.22 ± 57%  perf-profile.self.cycles-pp.__update_load_avg_se
      0.27            -0.2        0.10 ±  4%      -0.1        0.20 ± 57%  perf-profile.self.cycles-pp.syscall_return_via_sysret
      0.25            -0.2        0.08 ±  5%      -0.1        0.18 ± 57%  perf-profile.self.cycles-pp.__check_object_size
      0.26            -0.2        0.11 ±  4%      -0.1        0.19 ± 57%  perf-profile.self.cycles-pp.place_entity
      0.45            -0.2        0.30 ±  3%      -0.1        0.34 ± 57%  perf-profile.self.cycles-pp.enqueue_entity
      0.44            -0.2        0.29 ±  5%      -0.1        0.33 ± 57%  perf-profile.self.cycles-pp.enqueue_task_fair
      0.24            -0.2        0.09            -0.1        0.18 ± 57%  perf-profile.self.cycles-pp.wake_up_q
      0.24            -0.1        0.09 ±  5%      -0.1        0.18 ± 57%  perf-profile.self.cycles-pp.___perf_sw_event
      0.22 ±  2%      -0.1        0.07            -0.0        0.17 ± 59%  perf-profile.self.cycles-pp.entry_SYSCALL_64_after_hwframe
      0.32            -0.1        0.18 ±  2%      -0.1        0.24 ± 57%  perf-profile.self.cycles-pp.dequeue_task_fair
      0.21 ±  2%      -0.1        0.06 ±  7%      -0.1        0.15 ± 57%  perf-profile.self.cycles-pp.read_tsc
      0.20            -0.1        0.06            -0.1        0.15 ± 57%  perf-profile.self.cycles-pp.__rdgsbase_inactive
      0.36            -0.1        0.22 ±  4%      -0.1        0.26 ± 57%  perf-profile.self.cycles-pp.perf_tp_event
      0.28            -0.1        0.14            -0.1        0.21 ± 57%  perf-profile.self.cycles-pp.__kmalloc_node_noprof
      0.24            -0.1        0.11 ±  4%      -0.1        0.18 ± 57%  perf-profile.self.cycles-pp.strnlen
      0.21            -0.1        0.08 ±  6%      -0.0        0.16 ± 57%  perf-profile.self.cycles-pp.load_msg
      0.33            -0.1        0.20 ±  4%      -0.1        0.24 ± 57%  perf-profile.self.cycles-pp.attach_entity_load_avg
      0.12 ±  3%      -0.1        0.00            -0.1        0.05 ± 58%  perf-profile.self.cycles-pp.__do_notify
      0.44            -0.1        0.33            -0.1        0.33 ± 57%  perf-profile.self.cycles-pp.menu_select
      0.41            -0.1        0.29            -0.1        0.30 ± 57%  perf-profile.self.cycles-pp.update_se
      0.27            -0.1        0.15 ±  6%      -0.1        0.20 ± 57%  perf-profile.self.cycles-pp.select_task_rq
      0.17 ±  2%      -0.1        0.06 ±  8%      -0.1        0.12 ± 57%  perf-profile.self.cycles-pp.entry_SYSCALL_64_safe_stack
      0.23 ±  2%      -0.1        0.11 ±  4%      -0.1        0.16 ± 57%  perf-profile.self.cycles-pp.__flush_smp_call_function_queue
      0.19            -0.1        0.08 ±  6%      -0.1        0.14 ± 57%  perf-profile.self.cycles-pp.update_rq_clock
      0.20 ±  2%      -0.1        0.09            -0.0        0.16 ± 57%  perf-profile.self.cycles-pp.check_stack_object
      0.18 ±  2%      -0.1        0.06 ±  7%      -0.0        0.14 ± 57%  perf-profile.self.cycles-pp.wake_q_add_safe
      0.18            -0.1        0.07            +0.2        0.38 ± 57%  perf-profile.self.cycles-pp.__account_obj_stock
      0.21 ±  2%      -0.1        0.10 ±  4%      -0.0        0.16 ± 57%  perf-profile.self.cycles-pp.__kmalloc_cache_noprof
      0.24            -0.1        0.14            -0.1        0.18 ± 57%  perf-profile.self.cycles-pp.__dequeue_entity
      0.19            -0.1        0.10 ±  4%      -0.0        0.14 ± 57%  perf-profile.self.cycles-pp.pick_task_fair
      0.14 ±  3%      -0.1        0.05            -0.0        0.11 ± 57%  perf-profile.self.cycles-pp.inode_set_ctime_current
      0.16 ±  3%      -0.1        0.06 ±  7%      -0.0        0.11 ± 57%  perf-profile.self.cycles-pp.nohz_run_idle_balance
      0.16            -0.1        0.07            -0.0        0.12 ± 57%  perf-profile.self.cycles-pp.schedule_idle
      0.15            -0.1        0.06            -0.0        0.11 ± 57%  perf-profile.self.cycles-pp.avg_vruntime
      0.08            -0.1        0.00            -0.0        0.05 ± 57%  perf-profile.self.cycles-pp.security_msg_msg_free
      0.07            -0.1        0.00            -0.0        0.04 ± 57%  perf-profile.self.cycles-pp.ct_kernel_enter
      0.15            -0.1        0.08 ±  5%      -0.0        0.11 ± 57%  perf-profile.self.cycles-pp.dl_server_update
      0.15            -0.1        0.08 ±  5%      -0.0        0.11 ± 57%  perf-profile.self.cycles-pp.raw_spin_rq_lock_nested
      0.12            -0.1        0.05 ±  8%      -0.0        0.09 ± 57%  perf-profile.self.cycles-pp.migrate_disable_switch
      0.12 ±  4%      -0.1        0.05            -0.0        0.09 ± 57%  perf-profile.self.cycles-pp.__x64_sys_mq_timedreceive
      0.13            -0.1        0.07 ±  7%      -0.0        0.09 ± 57%  perf-profile.self.cycles-pp.___task_rq_lock
      0.09 ±  5%      -0.1        0.03 ± 70%      -0.0        0.08 ± 57%  perf-profile.self.cycles-pp.inode_set_ctime_to_ts
      0.22            -0.1        0.16            -0.1        0.16 ± 57%  perf-profile.self.cycles-pp.cpuidle_enter_state
      0.12            -0.1        0.06            -0.0        0.08 ± 57%  perf-profile.self.cycles-pp.store_msg
      0.12            -0.1        0.06            -0.0        0.08 ± 57%  perf-profile.self.cycles-pp.wakeup_preempt_fair
      0.11 ±  4%      -0.1        0.05 ±  8%      -0.0        0.08 ± 57%  perf-profile.self.cycles-pp.ktime_get_coarse_real_ts64_mg
      0.11            -0.1        0.05            -0.0        0.09 ± 57%  perf-profile.self.cycles-pp.timestamp_truncate
      0.12 ±  4%      -0.1        0.06 ±  7%      -0.0        0.09 ± 57%  perf-profile.self.cycles-pp.do_perf_trace_sched_stat_runtime
      0.11 ±  4%      -0.1        0.06 ±  8%      -0.0        0.08 ± 57%  perf-profile.self.cycles-pp.tracing_gen_ctx_irq_test
      0.13 ±  3%      -0.0        0.08            -0.0        0.10 ± 57%  perf-profile.self.cycles-pp.update_curr_dl_se
      0.15            -0.0        0.11 ±  4%      -0.0        0.11 ± 57%  perf-profile.self.cycles-pp.sched_balance_newidle
      0.09            -0.0        0.05 ±  8%      -0.0        0.07 ± 57%  perf-profile.self.cycles-pp.native_irq_return_iret
      0.14 ±  3%      -0.0        0.10 ±  4%      -0.0        0.10 ± 57%  perf-profile.self.cycles-pp.vruntime_eligible
      0.14 ±  3%      -0.0        0.11            -0.0        0.10 ± 57%  perf-profile.self.cycles-pp.ktime_get
      0.07 ±  7%      -0.0        0.03 ± 70%      -0.0        0.04 ± 57%  perf-profile.self.cycles-pp.__set_next_task_fair
      0.15 ±  3%      -0.0        0.13            -0.0        0.12 ± 57%  perf-profile.self.cycles-pp.put_prev_entity
      0.11 ±  4%      +0.0        0.12 ±  3%      -0.0        0.08 ± 57%  perf-profile.self.cycles-pp.set_next_task_idle
      0.06            +0.0        0.08 ±  6%      -0.0        0.04 ± 57%  perf-profile.self.cycles-pp.perf_swevent_event
      0.07 ±  7%      +0.0        0.09 ± 10%      +0.0        0.07 ± 58%  perf-profile.self.cycles-pp.mq_timedsend
      0.00            +0.1        0.05            +0.0        0.00        perf-profile.self.cycles-pp.ct_idle_enter
      0.00            +0.1        0.05            +0.0        0.00        perf-profile.self.cycles-pp.perf_trace_sched_switch
      0.00            +0.1        0.06            +0.0        0.00        perf-profile.self.cycles-pp.sched_update_worker
      0.12            +0.1        0.18 ±  6%      -0.0        0.09 ± 57%  perf-profile.self.cycles-pp.x64_sys_call
      0.38 ±  2%      +0.1        0.46            -0.1        0.28 ± 57%  perf-profile.self.cycles-pp.finish_task_switch
      0.08 ±  5%      +0.1        0.20 ±  6%      -0.0        0.06 ± 57%  perf-profile.self.cycles-pp.do_perf_trace_sched_switch
      0.18 ±  5%      +0.5        0.71 ±  3%      -0.0        0.14 ± 57%  perf-profile.self.cycles-pp.update_sg_lb_stats
      5.89            +0.9        6.81            -1.5        4.43 ± 57%  perf-profile.self.cycles-pp.intel_idle
      0.07            +7.4        7.48 ±  4%      +0.1        0.14 ± 57%  perf-profile.self.cycles-pp.__refill_obj_stock
      0.00            +8.7        8.70 ±  3%      +0.0        0.00        perf-profile.self.cycles-pp.drain_obj_stock
      2.25           +12.4       14.61            -0.3        1.90 ± 57%  perf-profile.self.cycles-pp.__memcg_slab_free_hook
      0.53           +19.0       19.48            +0.1        0.60 ± 57%  perf-profile.self.cycles-pp.__memcg_slab_post_alloc_hook


> 
> > > 
> > > From: Shakeel Butt <shakeel.butt@linux.dev>
> > > Subject: [PATCH] memcg: shrink obj_stock_pcp and cache multiple objcgs
> > > 
> > > 
> > > Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> > > ---
> > >  mm/memcontrol.c | 213 +++++++++++++++++++++++++++++++++++-------------
> > >  1 file changed, 156 insertions(+), 57 deletions(-)
> > > 
> > > diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> > > index d978e18b9b2d..2a9e5136a956 100644
> > > --- a/mm/memcontrol.c
> > > +++ b/mm/memcontrol.c
> > > @@ -150,14 +150,14 @@ static void obj_cgroup_release(struct percpu_ref *ref)
> > >  	 * However, it can be PAGE_SIZE or (x * PAGE_SIZE).
> > >  	 *
> > >  	 * The following sequence can lead to it:
> > > -	 * 1) CPU0: objcg == stock->cached_objcg
> > > +	 * 1) CPU0: objcg cached in one of stock->cached[i]
> > >  	 * 2) CPU1: we do a small allocation (e.g. 92 bytes),
> > >  	 *          PAGE_SIZE bytes are charged
> > >  	 * 3) CPU1: a process from another memcg is allocating something,
> > >  	 *          the stock if flushed,
> > >  	 *          objcg->nr_charged_bytes = PAGE_SIZE - 92
> > >  	 * 5) CPU0: we do release this object,
> > > -	 *          92 bytes are added to stock->nr_bytes
> > > +	 *          92 bytes are added to stock->nr_bytes[i]
> > >  	 * 6) CPU0: stock is flushed,
> > >  	 *          92 bytes are added to objcg->nr_charged_bytes
> > >  	 *
> > > @@ -2017,13 +2017,25 @@ static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
> > >  	.lock = INIT_LOCAL_TRYLOCK(lock),
> > >  };
> > >  
> > > +/*
> > > + * NR_OBJ_STOCK is sized so the entire hot path of obj_stock_pcp
> > > + * (lock, accounting metadata, nr_bytes[] and cached[]) fits within a
> > > + * single 64-byte cache line on non-debug 64-bit builds. With 5 slots:
> > > + *   lock(1) + index(1) + node_id(2) + slab stats(4) + nr_bytes(10)
> > > + *   + pad(6) + cached(40) == 64 bytes.
> > > + * A CPU can thus consume/refill/account against five different objcgs
> > > + * (typically per-node variants of the same memcg) while incurring at
> > > + * most one cache miss on the stock.
> > > + */
> > > +#define NR_OBJ_STOCK 5
> > >  struct obj_stock_pcp {
> > >  	local_trylock_t lock;
> > > -	unsigned int nr_bytes;
> > > -	struct obj_cgroup *cached_objcg;
> > > -	struct pglist_data *cached_pgdat;
> > > -	int nr_slab_reclaimable_b;
> > > -	int nr_slab_unreclaimable_b;
> > > +	int8_t index;
> > > +	int16_t node_id;
> > > +	int16_t nr_slab_reclaimable_b;
> > > +	int16_t nr_slab_unreclaimable_b;
> > > +	uint16_t nr_bytes[NR_OBJ_STOCK];
> > > +	struct obj_cgroup *cached[NR_OBJ_STOCK];
> > >  
> > >  	struct work_struct work;
> > >  	unsigned long flags;
> > > @@ -2031,10 +2043,13 @@ struct obj_stock_pcp {
> > >  
> > >  static DEFINE_PER_CPU_ALIGNED(struct obj_stock_pcp, obj_stock) = {
> > >  	.lock = INIT_LOCAL_TRYLOCK(lock),
> > > +	.index = -1,
> > > +	.node_id = NUMA_NO_NODE,
> > >  };
> > >  
> > >  static DEFINE_MUTEX(percpu_charge_mutex);
> > >  
> > > +static void drain_obj_stock_slot(struct obj_stock_pcp *stock, int i);
> > >  static void drain_obj_stock(struct obj_stock_pcp *stock);
> > >  static bool obj_stock_flush_required(struct obj_stock_pcp *stock,
> > >  				     struct mem_cgroup *root_memcg);
> > > @@ -3152,39 +3167,68 @@ static void unlock_stock(struct obj_stock_pcp *stock)
> > >  		local_unlock(&obj_stock.lock);
> > >  }
> > >  
> > > -/* Call after __refill_obj_stock() to ensure stock->cached_objg == objcg */
> > > +/* Call after __refill_obj_stock() so a slot for objcg exists in the stock */
> > >  static void __account_obj_stock(struct obj_cgroup *objcg,
> > >  				struct obj_stock_pcp *stock, int nr,
> > >  				struct pglist_data *pgdat, enum node_stat_item idx)
> > >  {
> > > -	int *bytes;
> > > +	int16_t *bytes;
> > > +	int i;
> > >  
> > > -	if (!stock || READ_ONCE(stock->cached_objcg) != objcg)
> > > +	/*
> > > +	 * node_id is stored as int16_t and -1 is used as the "no pgdat
> > > +	 * cached" sentinel, so MAX_NUMNODES must fit in a positive int16_t.
> > > +	 */
> > > +	BUILD_BUG_ON(MAX_NUMNODES >= S16_MAX);
> > > +
> > > +	if (!stock)
> > > +		goto direct;
> > > +
> > > +	for (i = 0; i < NR_OBJ_STOCK; ++i) {
> > > +		if (READ_ONCE(stock->cached[i]) == objcg)
> > > +			break;
> > > +	}
> > > +	if (i == NR_OBJ_STOCK)
> > >  		goto direct;
> > >  
> > >  	/*
> > >  	 * Save vmstat data in stock and skip vmstat array update unless
> > > -	 * accumulating over a page of vmstat data or when pgdat changes.
> > > +	 * accumulating over a page of vmstat data or when the objcg slot or
> > > +	 * pgdat the stats belong to changes.
> > >  	 */
> > > -	if (stock->cached_pgdat != pgdat) {
> > > -		/* Flush the existing cached vmstat data */
> > > -		struct pglist_data *oldpg = stock->cached_pgdat;
> > > +	if (stock->index < 0) {
> > > +		stock->index = i;
> > > +		stock->node_id = pgdat->node_id;
> > > +	} else if (stock->index != i || stock->node_id != pgdat->node_id) {
> > > +		struct obj_cgroup *old = READ_ONCE(stock->cached[stock->index]);
> > > +		struct pglist_data *oldpg = NODE_DATA(stock->node_id);
> > >  
> > >  		if (stock->nr_slab_reclaimable_b) {
> > > -			mod_objcg_mlstate(objcg, oldpg, NR_SLAB_RECLAIMABLE_B,
> > > +			mod_objcg_mlstate(old, oldpg, NR_SLAB_RECLAIMABLE_B,
> > >  					  stock->nr_slab_reclaimable_b);
> > >  			stock->nr_slab_reclaimable_b = 0;
> > >  		}
> > >  		if (stock->nr_slab_unreclaimable_b) {
> > > -			mod_objcg_mlstate(objcg, oldpg, NR_SLAB_UNRECLAIMABLE_B,
> > > +			mod_objcg_mlstate(old, oldpg, NR_SLAB_UNRECLAIMABLE_B,
> > >  					  stock->nr_slab_unreclaimable_b);
> > >  			stock->nr_slab_unreclaimable_b = 0;
> > >  		}
> > > -		stock->cached_pgdat = pgdat;
> > > +		stock->index = i;
> > > +		stock->node_id = pgdat->node_id;
> > >  	}
> > >  
> > >  	bytes = (idx == NR_SLAB_RECLAIMABLE_B) ? &stock->nr_slab_reclaimable_b
> > >  					       : &stock->nr_slab_unreclaimable_b;
> > > +	/*
> > > +	 * Cached stats are int16_t; flush directly if accumulating @nr would
> > > +	 * overflow or underflow the cache.
> > > +	 */
> > > +	if (abs(nr + *bytes) >= S16_MAX) {
> > > +		nr += *bytes;
> > > +		*bytes = 0;
> > > +		goto direct;
> > > +	}
> > > +
> > >  	/*
> > >  	 * Even for large object >= PAGE_SIZE, the vmstat data will still be
> > >  	 * cached locally at least once before pushing it out.
> > > @@ -3210,10 +3254,16 @@ static bool __consume_obj_stock(struct obj_cgroup *objcg,
> > >  				struct obj_stock_pcp *stock,
> > >  				unsigned int nr_bytes)
> > >  {
> > > -	if (objcg == READ_ONCE(stock->cached_objcg) &&
> > > -	    stock->nr_bytes >= nr_bytes) {
> > > -		stock->nr_bytes -= nr_bytes;
> > > -		return true;
> > > +	int i;
> > > +
> > > +	for (i = 0; i < NR_OBJ_STOCK; ++i) {
> > > +		if (READ_ONCE(stock->cached[i]) != objcg)
> > > +			continue;
> > > +		if (stock->nr_bytes[i] >= nr_bytes) {
> > > +			stock->nr_bytes[i] -= nr_bytes;
> > > +			return true;
> > > +		}
> > > +		return false;
> > >  	}
> > >  
> > >  	return false;
> > > @@ -3234,16 +3284,42 @@ static bool consume_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes)
> > >  	return ret;
> > >  }
> > >  
> > > -static void drain_obj_stock(struct obj_stock_pcp *stock)
> > > +/* Flush the cached slab stats (if any) back to their owning objcg/pgdat. */
> > > +static void drain_obj_stock_stats(struct obj_stock_pcp *stock)
> > > +{
> > > +	struct obj_cgroup *old;
> > > +	struct pglist_data *oldpg;
> > > +
> > > +	if (stock->index < 0)
> > > +		return;
> > > +
> > > +	old = READ_ONCE(stock->cached[stock->index]);
> > > +	oldpg = NODE_DATA(stock->node_id);
> > > +
> > > +	if (stock->nr_slab_reclaimable_b) {
> > > +		mod_objcg_mlstate(old, oldpg, NR_SLAB_RECLAIMABLE_B,
> > > +				  stock->nr_slab_reclaimable_b);
> > > +		stock->nr_slab_reclaimable_b = 0;
> > > +	}
> > > +	if (stock->nr_slab_unreclaimable_b) {
> > > +		mod_objcg_mlstate(old, oldpg, NR_SLAB_UNRECLAIMABLE_B,
> > > +				  stock->nr_slab_unreclaimable_b);
> > > +		stock->nr_slab_unreclaimable_b = 0;
> > > +	}
> > > +	stock->index = -1;
> > > +	stock->node_id = NUMA_NO_NODE;
> > > +}
> > > +
> > > +static void drain_obj_stock_slot(struct obj_stock_pcp *stock, int i)
> > >  {
> > > -	struct obj_cgroup *old = READ_ONCE(stock->cached_objcg);
> > > +	struct obj_cgroup *old = READ_ONCE(stock->cached[i]);
> > >  
> > >  	if (!old)
> > >  		return;
> > >  
> > > -	if (stock->nr_bytes) {
> > > -		unsigned int nr_pages = stock->nr_bytes >> PAGE_SHIFT;
> > > -		unsigned int nr_bytes = stock->nr_bytes & (PAGE_SIZE - 1);
> > > +	if (stock->nr_bytes[i]) {
> > > +		unsigned int nr_pages = stock->nr_bytes[i] >> PAGE_SHIFT;
> > > +		unsigned int nr_bytes = stock->nr_bytes[i] & (PAGE_SIZE - 1);
> > >  
> > >  		if (nr_pages) {
> > >  			struct mem_cgroup *memcg;
> > > @@ -3269,44 +3345,43 @@ static void drain_obj_stock(struct obj_stock_pcp *stock)
> > >  		 * so it might be changed in the future.
> > >  		 */
> > >  		atomic_add(nr_bytes, &old->nr_charged_bytes);
> > > -		stock->nr_bytes = 0;
> > > +		stock->nr_bytes[i] = 0;
> > >  	}
> > >  
> > > -	/*
> > > -	 * Flush the vmstat data in current stock
> > > -	 */
> > > -	if (stock->nr_slab_reclaimable_b || stock->nr_slab_unreclaimable_b) {
> > > -		if (stock->nr_slab_reclaimable_b) {
> > > -			mod_objcg_mlstate(old, stock->cached_pgdat,
> > > -					  NR_SLAB_RECLAIMABLE_B,
> > > -					  stock->nr_slab_reclaimable_b);
> > > -			stock->nr_slab_reclaimable_b = 0;
> > > -		}
> > > -		if (stock->nr_slab_unreclaimable_b) {
> > > -			mod_objcg_mlstate(old, stock->cached_pgdat,
> > > -					  NR_SLAB_UNRECLAIMABLE_B,
> > > -					  stock->nr_slab_unreclaimable_b);
> > > -			stock->nr_slab_unreclaimable_b = 0;
> > > -		}
> > > -		stock->cached_pgdat = NULL;
> > > -	}
> > > +	/* Flush vmstat data when its owning slot is being drained. */
> > > +	if (stock->index == i)
> > > +		drain_obj_stock_stats(stock);
> > >  
> > > -	WRITE_ONCE(stock->cached_objcg, NULL);
> > > +	WRITE_ONCE(stock->cached[i], NULL);
> > >  	obj_cgroup_put(old);
> > >  }
> > >  
> > > +static void drain_obj_stock(struct obj_stock_pcp *stock)
> > > +{
> > > +	int i;
> > > +
> > > +	for (i = 0; i < NR_OBJ_STOCK; ++i)
> > > +		drain_obj_stock_slot(stock, i);
> > > +}
> > > +
> > >  static bool obj_stock_flush_required(struct obj_stock_pcp *stock,
> > >  				     struct mem_cgroup *root_memcg)
> > >  {
> > > -	struct obj_cgroup *objcg = READ_ONCE(stock->cached_objcg);
> > > +	struct obj_cgroup *objcg;
> > >  	struct mem_cgroup *memcg;
> > >  	bool flush = false;
> > > +	int i;
> > >  
> > >  	rcu_read_lock();
> > > -	if (objcg) {
> > > +	for (i = 0; i < NR_OBJ_STOCK; ++i) {
> > > +		objcg = READ_ONCE(stock->cached[i]);
> > > +		if (!objcg)
> > > +			continue;
> > >  		memcg = obj_cgroup_memcg(objcg);
> > > -		if (memcg && mem_cgroup_is_descendant(memcg, root_memcg))
> > > +		if (memcg && mem_cgroup_is_descendant(memcg, root_memcg)) {
> > >  			flush = true;
> > > +			break;
> > > +		}
> > >  	}
> > >  	rcu_read_unlock();
> > >  
> > > @@ -3319,6 +3394,8 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
> > >  			       bool allow_uncharge)
> > >  {
> > >  	unsigned int nr_pages = 0;
> > > +	unsigned int stock_nr_bytes;
> > > +	int i, slot = -1, empty_slot = -1;
> > >  
> > >  	if (!stock) {
> > >  		nr_pages = nr_bytes >> PAGE_SHIFT;
> > > @@ -3327,21 +3404,43 @@ static void __refill_obj_stock(struct obj_cgroup *objcg,
> > >  		goto out;
> > >  	}
> > >  
> > > -	if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */
> > > -		drain_obj_stock(stock);
> > > +	for (i = 0; i < NR_OBJ_STOCK; ++i) {
> > > +		struct obj_cgroup *cached = READ_ONCE(stock->cached[i]);
> > > +
> > > +		if (!cached) {
> > > +			if (empty_slot == -1)
> > > +				empty_slot = i;
> > > +			continue;
> > > +		}
> > > +		if (cached == objcg) {
> > > +			slot = i;
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	if (slot == -1) {
> > > +		slot = empty_slot;
> > > +		if (slot == -1) {
> > > +			slot = get_random_u32_below(NR_OBJ_STOCK);
> > > +			drain_obj_stock_slot(stock, slot);
> > > +		}
> > >  		obj_cgroup_get(objcg);
> > > -		stock->nr_bytes = atomic_read(&objcg->nr_charged_bytes)
> > > +		stock->nr_bytes[slot] = atomic_read(&objcg->nr_charged_bytes)
> > >  				? atomic_xchg(&objcg->nr_charged_bytes, 0) : 0;
> > > -		WRITE_ONCE(stock->cached_objcg, objcg);
> > > +		WRITE_ONCE(stock->cached[slot], objcg);
> > >  
> > >  		allow_uncharge = true;	/* Allow uncharge when objcg changes */
> > >  	}
> > > -	stock->nr_bytes += nr_bytes;
> > >  
> > > -	if (allow_uncharge && (stock->nr_bytes > PAGE_SIZE)) {
> > > -		nr_pages = stock->nr_bytes >> PAGE_SHIFT;
> > > -		stock->nr_bytes &= (PAGE_SIZE - 1);
> > > +	stock_nr_bytes = (unsigned int)stock->nr_bytes[slot] + nr_bytes;
> > > +
> > > +	/* nr_bytes[] is uint16_t; flush if we would refill >= U16_MAX. */
> > > +	if ((allow_uncharge && (stock_nr_bytes > PAGE_SIZE)) ||
> > > +	    stock_nr_bytes >= U16_MAX) {
> > > +		nr_pages = stock_nr_bytes >> PAGE_SHIFT;
> > > +		stock_nr_bytes &= (PAGE_SIZE - 1);
> > >  	}
> > > +	stock->nr_bytes[slot] = stock_nr_bytes;
> > >  
> > >  out:
> > >  	if (nr_pages)
> > > -- 
> > > 2.53.0-Meta
> > > 

^ permalink raw reply

* Re: [PATCH v3] memcg: cache obj_stock by memcg, not by objcg pointer
From: Harry Yoo @ 2026-05-19 23:39 UTC (permalink / raw)
  To: Shakeel Butt
  Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Muchun Song, Qi Zheng, Alexandre Ghiti, Joshua Hahn,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <agynzVVBb4CYJTYG@linux.dev>



On 5/20/26 5:11 AM, Shakeel Butt wrote:
> On Wed, May 20, 2026 at 12:00:16AM +0900, Harry Yoo wrote:
>>
>>
> [...]
>>>
>>> The full clean solution might take one more cycle and I think we can not just
>>> ignore 67% regression on 7.1.
>>
>> That is valid point, unfortunately.
>>
>> One more thing I have to ask... for v7.1, wouldn't it be a safer option to
>> revert the per-node object change and re-introduce it once we have a cleaner
>> solution?
> 
> The issue with that revert is that we reintroduce all node lru locking in the
> objcg reparenting path.

I'm not sure the problems with all-node locking are serious enough to 
rule it out as an option for 7.1.

It is not ideal, but given that the critical section for reparenting is 
independent of folio count, would this actually be a significant problem 
in practice? (even large servers rarely go beyond 8 NUMA nodes...)

>> This change was introduced in v5, but the implementation before v4 had been
>> exposed in -next for a while, and I think we don't have enough justification
>> to keep the per-node objcgs change, at least for v7.1, given that we have an
>> unexpected last-minute regression and
>> correctness concerns (albeit slight).
> 
> I am waiting for Oliver to test the multi-objcg patch I sent. If that also
> resolves the regression then we have one more option i.e. backport that to 7.1
> to fix the regression.

Yeah, If per-node objcg is required in future kernels anyway, this 
option would be more ideal (if available).

> So to summarize, for future kernels we will be having multi-objcg in some form.
> For 7.1, we have to decide between (1) do nothing (2) this patch or (3) backport
> the multi-objcg path to 7.1.

Ack.

Thanks!

-- 
Cheers,
Harry / Hyeonggon


^ permalink raw reply

* Re: [BULK] Re: [RFC PATCH v5 20/29] sched/deadline: Allow deeper hierarchies of RT cgroups
From: luca abeni @ 2026-05-19 21:02 UTC (permalink / raw)
  To: Tejun Heo
  Cc: Yuri Andriaccio, Peter Zijlstra, Yuri Andriaccio, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, linux-kernel, hannes,
	mkoutny, cgroups
In-Reply-To: <agteySwOHszMVMp8@slm.duckdns.org>

Hi,

I think we are converging... But I still have some doubts (probably due
to the fact that I do not know the cgroup v2 API well):


On Mon, 18 May 2026 08:47:37 -1000
Tejun Heo <tj@kernel.org> wrote:
[...]
> I wonder whether it can be generalized more. Would something like the
> following work? I'm going to ignore period for the sake of simplicity
> as it doesn't seem to affect admission decisions.
> 
> - There is no root cgroup.rt.max in line with other control knobs.

Well, the reason we had "rt.{runtime,period}_us" (now "rt.max") in the
root cgroup is that RT cgroups are scheduled by dl entities (one dl
entity per cpu), and these dl entities must be accounted for in the
SCHED_DEADLINE admission test... The easiest way to do this is to
reserve a fixed fraction of the CPU time to RT cgroups, leaving the
remaining fraction to SCHED_DEADLINE tasks. And we used rt.max to
configure the fraction of CPU time reserved for RT cgroups; do you have
suggestions about alternative interfaces for this configuration?


> - max means running in the nearest ancestor that has budget
> configuration. Obviously, if no one has budget configured, run in
> root.

Uh... OK; I understand this, now, but I suspect this will increase the
complexity of the admission control... Yuri, what do you think?


> - Setting a budget is subject to admission control in both directions
> - the budget source (the nearest budgeted ancestor, or the root pool
> if none) should have enough to give out and the target budget should
> be big enough to contain the actual usages and !max descendants in
> the subtree. Going to max is always fine - the source previously gave
> the budget out, so it has room to take everything back.

OK... Just to understand: if we consider this situation
	root cgroup -> G1 (50, 100) -> G2 (10, 100)
and G1 switches to "max", what happens to G2? Does it stay (10, 100),
or is it forced to switch to "max", too?


I was thinking about enforcing that a cgroup can have runtime > 0 only
if it is a direct child of the root cgroup, or if its parent has
runtime > 0 and is not "max" (so, in the previous example G1 can switch
to "max" only if G2 sets its runtime to 0). Could this be acceptable?


			Thanks,
				Luca


> 
> It seems like the above would give fairly generic behavior without
> abrupt system-wide switches while staying relatively close to the
> behaviors of other resource knobs. I could be missing something tho.
> Would something like this work?
> 
> Thanks.
> 


^ permalink raw reply

* Re: [PATCH v3] memcg: cache obj_stock by memcg, not by objcg pointer
From: Andrew Morton @ 2026-05-19 20:49 UTC (permalink / raw)
  To: Shakeel Butt
  Cc: Harry Yoo, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Muchun Song, Qi Zheng, Alexandre Ghiti, Joshua Hahn,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <agynzVVBb4CYJTYG@linux.dev>

On Tue, 19 May 2026 13:11:13 -0700 Shakeel Butt <shakeel.butt@linux.dev> wrote:

> Andrew, please don't send this patch to Linus until we decide on the option.

No probs, I added a note-to-self.

^ permalink raw reply

* Re: [PATCH v3] memcg: cache obj_stock by memcg, not by objcg pointer
From: Shakeel Butt @ 2026-05-19 20:11 UTC (permalink / raw)
  To: Harry Yoo
  Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Roman Gushchin,
	Muchun Song, Qi Zheng, Alexandre Ghiti, Joshua Hahn,
	Meta kernel team, linux-mm, cgroups, linux-kernel,
	kernel test robot
In-Reply-To: <ca8e655d-5fe7-4957-8a36-6667616be8b6@kernel.org>

On Wed, May 20, 2026 at 12:00:16AM +0900, Harry Yoo wrote:
> 
> 
[...]
> > 
> > The full clean solution might take one more cycle and I think we can not just
> > ignore 67% regression on 7.1.
> 
> That is valid point, unfortunately.
> 
> One more thing I have to ask... for v7.1, wouldn't it be a safer option to
> revert the per-node object change and re-introduce it once we have a cleaner
> solution?

The issue with that revert is that we reintroduce all node lru locking in the
objcg reparenting path.

> 
> This change was introduced in v5, but the implementation before v4 had been
> exposed in -next for a while, and I think we don't have enough justification
> to keep the per-node objcgs change, at least for v7.1, given that we have an
> unexpected last-minute regression and
> correctness concerns (albeit slight).

I am waiting for Oliver to test the multi-objcg patch I sent. If that also
resolves the regression then we have one more option i.e. backport that to 7.1
to fix the regression.

So to summarize, for future kernels we will be having multi-objcg in some form.
For 7.1, we have to decide between (1) do nothing (2) this patch or (3) backport
the multi-objcg path to 7.1.

Andrew, please don't send this patch to Linus until we decide on the option.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox