From: Joshua Hahn <joshua.hahnjy@gmail.com>
To: Minchan Kim <minchan@kernel.org>,
Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>,
Yosry Ahmed <yosry.ahmed@linux.dev>,
Nhat Pham <hoangnhat.pham@linux.dev>,
Nhat Pham <nphamcs@gmail.com>, Harry Yoo <harry.yoo@oracle.com>,
Andrew Morton <akpm@linux-foundation.org>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
kernel-team@meta.com
Subject: [PATCH 10/11] mm/zsmalloc: Handle single object charge migration in migrate_zspage
Date: Wed, 11 Mar 2026 12:51:47 -0700 [thread overview]
Message-ID: <20260311195153.4013476-11-joshua.hahnjy@gmail.com> (raw)
In-Reply-To: <20260311195153.4013476-1-joshua.hahnjy@gmail.com>
In zsmalloc, there are two types of migrations: Migrations of single
compressed objects from one zspage to another, and substitutions of
zpdescs from zspages.
In both of these migrations, memcg association for the compressed
objects do not change. However, the physical location of the compressed
objects may change, which alters their lruvec association.
In this patch, handle the single compressed object migration and
transfer lruvec and node statistics across the affected lruvecs / nodes.
Zsmalloc compressed objects, like slab objects, can span two pages.
When a spanning object is migrated, possibly to another zspage where
it spans two zpdescs, up to 4 nodes can be touched.
Instead of enumerating all possible combinations of node migrations,
simply uncharge entirely from the source (1 or 2 nodes) and charge
entirely to the destination (1 or 2 nodes).
s_off d_off
v v
----------+ +---- -----+ +---------
... ooo ooo xx| |x oo ... --> ... ooo x| |xx ooo oo ...
----------+ +---- -----+ +---------
pg1 pg2 pg3 pg4
s_zspage d_zspage
To do this, calculate how much of the compressed object lives on each
page and perform up to 4 uncharge-charges.
Note that these operations cannot call the existing
zs_{charge, uncharge}_objcg functions we introduced, since we are
holding the class spin lock and obj_cgroup_charge can sleep.
Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
---
mm/zsmalloc.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 70 insertions(+), 4 deletions(-)
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index ab085961b0e2..f3508ff8b3ab 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -1684,15 +1684,81 @@ static unsigned long find_alloced_obj(struct size_class *class,
return handle;
}
+#ifdef CONFIG_MEMCG
static void zs_migrate_objcg(struct zspage *s_zspage, struct zspage *d_zspage,
- unsigned long used_obj, unsigned long free_obj)
+ unsigned long used_obj, unsigned long free_obj,
+ struct zs_pool *pool, int size)
{
- unsigned int s_idx = used_obj & OBJ_INDEX_MASK;
- unsigned int d_idx = free_obj & OBJ_INDEX_MASK;
+ struct zpdesc *s_zpdesc, *d_zpdesc;
+ struct obj_cgroup *objcg;
+ struct mem_cgroup *memcg;
+ struct lruvec *l;
+ unsigned int s_idx, d_idx;
+ unsigned int s_off, d_off;
+ int charges[4], nids[4], partial;
+ int s_bytes_in_page, d_bytes_in_page;
+ int i;
+
+ if (!cgroup_subsys_on_dfl(memory_cgrp_subsys))
+ goto out;
+
+ obj_to_location(used_obj, &s_zpdesc, &s_idx);
+ obj_to_location(free_obj, &d_zpdesc, &d_idx);
+
+ objcg = s_zspage->objcgs[s_idx];
+ if (!objcg)
+ goto out;
+
+ /*
+ * The object migration here can touch up to 4 nodes.
+ * Instead of breaking down all possible combinations of node changes,
+ * just uncharge entirely from the source and charge entirely to the
+ * destination, even if there is are node overlaps between src and dst.
+ */
+ s_off = (s_idx * size) % PAGE_SIZE;
+ d_off = (d_idx * size) % PAGE_SIZE;
+ s_bytes_in_page = min_t(int, size, PAGE_SIZE - s_off);
+ d_bytes_in_page = min_t(int, size, PAGE_SIZE - d_off);
+
+ charges[0] = -s_bytes_in_page;
+ nids[0] = page_to_nid(zpdesc_page(s_zpdesc));
+ charges[1] = -(size - s_bytes_in_page); /* 0 if object doesn't span */
+ if (charges[1])
+ nids[1] = page_to_nid(zpdesc_page(get_next_zpdesc(s_zpdesc)));
+
+ charges[2] = d_bytes_in_page;
+ nids[2] = page_to_nid(zpdesc_page(d_zpdesc));
+ charges[3] = size - d_bytes_in_page; /* 0 if object doesn't span */
+ if (charges[3])
+ nids[3] = page_to_nid(zpdesc_page(get_next_zpdesc(d_zpdesc)));
+ rcu_read_lock();
+ memcg = obj_cgroup_memcg(objcg);
+ for (i = 0; i < 4; i++) {
+ if (!charges[i])
+ continue;
+
+ l = mem_cgroup_lruvec(memcg, NODE_DATA(nids[i]));
+ partial = (PAGE_SIZE * charges[i]) / size;
+ mod_memcg_lruvec_state(l, pool->compressed_stat, charges[i]);
+ mod_memcg_lruvec_state(l, pool->uncompressed_stat, partial);
+ }
+ rcu_read_unlock();
+
+ dec_node_page_state(zpdesc_page(s_zpdesc), pool->uncompressed_stat);
+ inc_node_page_state(zpdesc_page(d_zpdesc), pool->uncompressed_stat);
+
+out:
d_zspage->objcgs[d_idx] = s_zspage->objcgs[s_idx];
s_zspage->objcgs[s_idx] = NULL;
}
+#else
+static void zs_migrate_objcg(struct zspage *s_zspage, struct zspage *d_zspage,
+ unsigned long used_obj, unsigned long free_obj,
+ struct zs_pool *pool, int size)
+{
+}
+#endif
static void migrate_zspage(struct zs_pool *pool, struct zspage *src_zspage,
struct zspage *dst_zspage)
@@ -1719,7 +1785,7 @@ static void migrate_zspage(struct zs_pool *pool, struct zspage *src_zspage,
if (pool->memcg_aware)
zs_migrate_objcg(src_zspage, dst_zspage,
- used_obj, free_obj);
+ used_obj, free_obj, pool, class->size);
obj_idx++;
obj_free(class->size, used_obj);
--
2.52.0
next prev parent reply other threads:[~2026-03-11 19:52 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-11 19:51 [PATCH 00/11] mm/zswap, zsmalloc: Per-memcg-lruvec zswap accounting Joshua Hahn
2026-03-11 19:51 ` [PATCH 01/11] mm/zsmalloc: Rename zs_object_copy to zs_obj_copy Joshua Hahn
2026-03-11 19:56 ` Yosry Ahmed
2026-03-11 20:00 ` Nhat Pham
2026-03-11 19:51 ` [PATCH 02/11] mm/zsmalloc: Make all obj_idx unsigned ints Joshua Hahn
2026-03-11 19:58 ` Yosry Ahmed
2026-03-11 20:01 ` Nhat Pham
2026-03-11 19:51 ` [PATCH 03/11] mm/zsmalloc: Introduce conditional memcg awareness to zs_pool Joshua Hahn
2026-03-11 20:12 ` Nhat Pham
2026-03-11 20:16 ` Johannes Weiner
2026-03-11 20:19 ` Yosry Ahmed
2026-03-11 20:20 ` Joshua Hahn
2026-03-11 19:51 ` [PATCH 04/11] mm/zsmalloc: Introduce objcgs pointer in struct zspage Joshua Hahn
2026-03-11 20:17 ` Nhat Pham
2026-03-11 20:22 ` Joshua Hahn
2026-03-11 19:51 ` [PATCH 05/11] mm/zsmalloc: Store obj_cgroup pointer in zspage Joshua Hahn
2026-03-11 20:17 ` Yosry Ahmed
2026-03-11 20:24 ` Joshua Hahn
2026-03-11 19:51 ` [PATCH 06/11] mm/zsmalloc, zswap: Redirect zswap_entry->objcg to zspage Joshua Hahn
2026-03-11 19:51 ` [PATCH 07/11] mm/zsmalloc, zswap: Handle objcg charging and lifetime in zsmalloc Joshua Hahn
2026-03-12 21:42 ` Johannes Weiner
2026-03-13 15:34 ` Joshua Hahn
2026-03-13 16:49 ` Johannes Weiner
2026-03-11 19:51 ` [PATCH 08/11] mm/memcontrol: Track MEMCG_ZSWAPPED in bytes Joshua Hahn
2026-03-11 20:33 ` Nhat Pham
2026-03-17 19:13 ` Joshua Hahn
2026-03-11 19:51 ` [PATCH 09/11] mm/vmstat, memcontrol: Track ZSWAP_B, ZSWAPPED_B per-memcg-lruvec Joshua Hahn
2026-03-11 19:51 ` Joshua Hahn [this message]
2026-03-12 3:51 ` [PATCH 10/11] mm/zsmalloc: Handle single object charge migration in migrate_zspage kernel test robot
2026-03-12 3:51 ` kernel test robot
2026-03-12 16:56 ` Joshua Hahn
2026-03-11 19:51 ` [PATCH 11/11] mm/zsmalloc: Handle charge migration in zpdesc substitution Joshua Hahn
2026-03-11 19:54 ` [PATCH 00/11] mm/zswap, zsmalloc: Per-memcg-lruvec zswap accounting Joshua Hahn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260311195153.4013476-11-joshua.hahnjy@gmail.com \
--to=joshua.hahnjy@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=hannes@cmpxchg.org \
--cc=harry.yoo@oracle.com \
--cc=hoangnhat.pham@linux.dev \
--cc=kernel-team@meta.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=minchan@kernel.org \
--cc=nphamcs@gmail.com \
--cc=senozhatsky@chromium.org \
--cc=yosry.ahmed@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox