Re: [PATCH v1 06/26] mm: memcontrol: return root object cgroup for root memory cgroup

From: Qi Zheng

Date: Wed Nov 19 2025 - 02:43:16 EST




On 11/19/25 3:24 PM, Harry Yoo wrote:
On Tue, Nov 18, 2025 at 08:11:04PM +0800, Qi Zheng wrote:


On 11/18/25 7:28 PM, Qi Zheng wrote:
Hi Harry,

On 11/17/25 5:17 PM, Harry Yoo wrote:
On Tue, Oct 28, 2025 at 09:58:19PM +0800, Qi Zheng wrote:
From: Muchun Song <songmuchun@xxxxxxxxxxxxx>

Memory cgroup functions such as get_mem_cgroup_from_folio() and
get_mem_cgroup_from_mm() return a valid memory cgroup pointer,
even for the root memory cgroup. In contrast, the situation for
object cgroups has been different.

Previously, the root object cgroup couldn't be returned because
it didn't exist. Now that a valid root object cgroup exists, for
the sake of consistency, it's necessary to align the behavior of
object-cgroup-related operations with that of memory cgroup APIs.

Signed-off-by: Muchun Song <songmuchun@xxxxxxxxxxxxx>
Signed-off-by: Qi Zheng <zhengqi.arch@xxxxxxxxxxxxx>
---
  include/linux/memcontrol.h | 29 +++++++++++++++++-------
  mm/memcontrol.c            | 45 ++++++++++++++++++++------------------
  mm/percpu.c                |  2 +-
  3 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 6185d8399a54e..9fdbd4970021d 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -332,6 +332,7 @@ struct mem_cgroup {
  #define MEMCG_CHARGE_BATCH 64U
  extern struct mem_cgroup *root_mem_cgroup;
+extern struct obj_cgroup *root_obj_cgroup;
  enum page_memcg_data_flags {
      /* page->memcg_data is a pointer to an slabobj_ext vector */
@@ -549,6 +550,11 @@ static inline bool
mem_cgroup_is_root(struct mem_cgroup *memcg)
      return (memcg == root_mem_cgroup);
  }
+static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg)
+{
+    return objcg == root_obj_cgroup;
+}

After reparenting, an objcg may satisfy objcg->memcg == root_mem_cgroup
while objcg != root_obj_cgroup. Should they be considered as
root objcgs?

Indeed, it's pointless to charge to root_mem_cgroup (objcg->memcg).

So it should be:

static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg)
{
    return (objcg == root_obj_cgroup) || (objcg->memcg ==
root_mem_cgroup);
}

Oh, we can't do that because we still need to consider this objcg when
uncharging. Some pages may be charged before reparenting.

Ouch, right. We don't know if it's charged before reparenting and so
it can break statistics in a few places if we skip uncharging it after
repareting.

Right.


And I think we don't charge new pages to objcgs that satisfy
(objcg->memcg == root_mem_cgroup) && (objcg != root_obj_cgroup)
after they're reparented anyway...

The charge and uncharge operations must be symmetrical, so we cannot
control the charge operation independently.

Otherwise:

charge
======

if ((objcg->memcg == root_mem_cgroup))
skip charge this page

uncharge
========

we can't decide whether to skip this page.

Thanks,
Qi