* [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
@ 2026-08-06 8:27 xueyuan.chen
2026-08-06 8:27 ` [RFC PATCH 1/2] mm: zsmalloc: add a page limit to pool compaction xueyuan.chen
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: xueyuan.chen @ 2026-08-06 8:27 UTC (permalink / raw)
To: Minchan Kim, Sergey Senozhatsky, Andrew Morton
Cc: linux-mm, linux-kernel, xueyuan.chen21
From: Xueyuan Chen <xueyuan.chen@vivo.com>
zs_shrinker_scan() currently calls zs_compact(), which compacts the whole
pool regardless of sc->nr_to_scan. A single shrinker callback can therefore
reclaim much more than requested and spend a long time in compaction.
On an Android device with 12 GB of RAM, observed zsmalloc compaction
durations had a p95 of 38.86 ms and a maximum of 269.68 ms, motivating a
per-scan reclaim goal.
This series uses sc->nr_to_scan as a reclaimed-page goal and keeps a
per-pool size-class cursor so later scans resume where the previous scan
stopped. The existing full-pool behavior of zs_compact() is unchanged.
Xueyuan Chen (2):
mm: zsmalloc: add a page limit to pool compaction
mm: zsmalloc: use the shrinker reclaim budget
mm/zsmalloc.c | 46 +++++++++++++++++++++++++++++++---------------
1 file changed, 31 insertions(+), 15 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 4+ messages in thread
* [RFC PATCH 1/2] mm: zsmalloc: add a page limit to pool compaction
2026-08-06 8:27 [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware xueyuan.chen
@ 2026-08-06 8:27 ` xueyuan.chen
2026-08-06 8:27 ` [RFC PATCH 2/2] mm: zsmalloc: use the shrinker reclaim budget xueyuan.chen
2026-08-07 3:56 ` [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware Sergey Senozhatsky
2 siblings, 0 replies; 4+ messages in thread
From: xueyuan.chen @ 2026-08-06 8:27 UTC (permalink / raw)
To: Minchan Kim, Sergey Senozhatsky, Andrew Morton
Cc: linux-mm, linux-kernel, xueyuan.chen21
From: Xueyuan Chen <xueyuan.chen@vivo.com>
Allow pool compaction to stop after reclaiming a requested number of
pages. This prepares it for budgeted callers while keeping zs_compact()
behavior unchanged.
Signed-off-by: Xueyuan Chen <xueyuan.chen@vivo.com>
---
mm/zsmalloc.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 83f5820c45f9..334d24093c64 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -1884,7 +1884,8 @@ static unsigned long zs_can_compact(struct size_class *class)
}
static unsigned long __zs_compact(struct zs_pool *pool,
- struct size_class *class)
+ struct size_class *class,
+ unsigned long max_pages)
{
struct zspage *src_zspage = NULL;
struct zspage *dst_zspage = NULL;
@@ -1896,7 +1897,7 @@ static unsigned long __zs_compact(struct zs_pool *pool,
*/
write_lock(&pool->lock);
spin_lock(&class->lock);
- while (zs_can_compact(class)) {
+ while ((pages_freed < max_pages) && zs_can_compact(class)) {
int fg;
if (!dst_zspage) {
@@ -1947,12 +1948,16 @@ static unsigned long __zs_compact(struct zs_pool *pool,
return pages_freed;
}
-unsigned long zs_compact(struct zs_pool *pool)
+static unsigned long zs_compact_pool(struct zs_pool *pool,
+ unsigned long max_pages)
{
int i;
struct size_class *class;
unsigned long pages_freed = 0;
+ if (!max_pages)
+ return 0;
+
/*
* Pool compaction is performed under pool->lock so it is basically
* single-threaded. Having more than one thread in __zs_compact()
@@ -1966,13 +1971,21 @@ unsigned long zs_compact(struct zs_pool *pool)
class = pool->size_class[i];
if (class->index != i)
continue;
- pages_freed += __zs_compact(pool, class);
+ pages_freed += __zs_compact(pool, class,
+ max_pages - pages_freed);
+ if (pages_freed >= max_pages)
+ break;
}
atomic_long_add(pages_freed, &pool->stats.pages_compacted);
atomic_set(&pool->compaction_in_progress, 0);
return pages_freed;
}
+
+unsigned long zs_compact(struct zs_pool *pool)
+{
+ return zs_compact_pool(pool, ULONG_MAX);
+}
EXPORT_SYMBOL_GPL(zs_compact);
void zs_pool_stats(struct zs_pool *pool, struct zs_pool_stats *stats)
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [RFC PATCH 2/2] mm: zsmalloc: use the shrinker reclaim budget
2026-08-06 8:27 [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware xueyuan.chen
2026-08-06 8:27 ` [RFC PATCH 1/2] mm: zsmalloc: add a page limit to pool compaction xueyuan.chen
@ 2026-08-06 8:27 ` xueyuan.chen
2026-08-07 3:56 ` [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware Sergey Senozhatsky
2 siblings, 0 replies; 4+ messages in thread
From: xueyuan.chen @ 2026-08-06 8:27 UTC (permalink / raw)
To: Minchan Kim, Sergey Senozhatsky, Andrew Morton
Cc: linux-mm, linux-kernel, xueyuan.chen21
From: Xueyuan Chen <xueyuan.chen@vivo.com>
The zsmalloc shrinker currently compacts the whole pool for every scan,
which can do much more work than reclaim requires.
Use the requested page count as the compaction goal and continue later
scans from the previous position.
Signed-off-by: Xueyuan Chen <xueyuan.chen@vivo.com>
---
mm/zsmalloc.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 334d24093c64..aa5900ce2620 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -217,6 +217,8 @@ struct zs_pool {
/* protect zspage migration/compaction */
rwlock_t lock;
atomic_t compaction_in_progress;
+ /* next class shrinker triggered compaction */
+ unsigned int compact_cursor;
};
static inline void zpdesc_set_first(struct zpdesc *zpdesc)
@@ -1949,9 +1951,10 @@ static unsigned long __zs_compact(struct zs_pool *pool,
}
static unsigned long zs_compact_pool(struct zs_pool *pool,
- unsigned long max_pages)
+ unsigned long max_pages,
+ bool use_cursor)
{
- int i;
+ unsigned int index, nr_scanned;
struct size_class *class;
unsigned long pages_freed = 0;
@@ -1967,15 +1970,19 @@ static unsigned long zs_compact_pool(struct zs_pool *pool,
if (atomic_xchg(&pool->compaction_in_progress, 1))
return 0;
- for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) {
- class = pool->size_class[i];
- if (class->index != i)
- continue;
- pages_freed += __zs_compact(pool, class,
- max_pages - pages_freed);
+ index = use_cursor ? pool->compact_cursor : ZS_SIZE_CLASSES - 1;
+ for (nr_scanned = ZS_SIZE_CLASSES; nr_scanned; nr_scanned--) {
+ class = pool->size_class[index];
+ if (class->index == index)
+ pages_freed += __zs_compact(pool, class,
+ max_pages - pages_freed);
+ index = index ? index - 1 : ZS_SIZE_CLASSES - 1;
if (pages_freed >= max_pages)
break;
}
+ if (use_cursor)
+ pool->compact_cursor = index;
+
atomic_long_add(pages_freed, &pool->stats.pages_compacted);
atomic_set(&pool->compaction_in_progress, 0);
@@ -1984,7 +1991,7 @@ static unsigned long zs_compact_pool(struct zs_pool *pool,
unsigned long zs_compact(struct zs_pool *pool)
{
- return zs_compact_pool(pool, ULONG_MAX);
+ return zs_compact_pool(pool, ULONG_MAX, false);
}
EXPORT_SYMBOL_GPL(zs_compact);
@@ -2000,12 +2007,7 @@ static unsigned long zs_shrinker_scan(struct shrinker *shrinker,
unsigned long pages_freed;
struct zs_pool *pool = shrinker->private_data;
- /*
- * Compact classes and calculate compaction delta.
- * Can run concurrently with a manually triggered
- * (by user) compaction.
- */
- pages_freed = zs_compact(pool);
+ pages_freed = zs_compact_pool(pool, sc->nr_to_scan, true);
return pages_freed ? pages_freed : SHRINK_STOP;
}
@@ -2094,6 +2096,7 @@ struct zs_pool *zs_create_pool(const char *name)
init_deferred_free(pool);
rwlock_init(&pool->lock);
atomic_set(&pool->compaction_in_progress, 0);
+ pool->compact_cursor = ZS_SIZE_CLASSES - 1;
pool->name = kstrdup(name, GFP_KERNEL);
if (!pool->name)
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-06 8:27 [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware xueyuan.chen
2026-08-06 8:27 ` [RFC PATCH 1/2] mm: zsmalloc: add a page limit to pool compaction xueyuan.chen
2026-08-06 8:27 ` [RFC PATCH 2/2] mm: zsmalloc: use the shrinker reclaim budget xueyuan.chen
@ 2026-08-07 3:56 ` Sergey Senozhatsky
2 siblings, 0 replies; 4+ messages in thread
From: Sergey Senozhatsky @ 2026-08-07 3:56 UTC (permalink / raw)
To: xueyuan.chen
Cc: Minchan Kim, Sergey Senozhatsky, Andrew Morton, linux-mm,
linux-kernel, xueyuan.chen21
On (26/08/06 16:27), xueyuan.chen@vivo.com wrote:
> On an Android device with 12 GB of RAM, observed zsmalloc compaction
> durations had a p95 of 38.86 ms and a maximum of 269.68 ms, motivating a
> per-scan reclaim goal.
Would it be possible to give a little more data? What was the
fragmentation ratio, how much memory was saved during that
auto-compaction, etc. If possible.
Somewhere in the back of my mind I was thinking about, maybe,
disabling (removing) zsmalloc shrinker callbacks, in other words
disabling auto-compaction. We have a sysfs knob for pool compaction
for system that still want to run compaction. So I'm leaning towards
removal of shrinker callbacks from zsmalloc.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-07 3:56 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 8:27 [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware xueyuan.chen
2026-08-06 8:27 ` [RFC PATCH 1/2] mm: zsmalloc: add a page limit to pool compaction xueyuan.chen
2026-08-06 8:27 ` [RFC PATCH 2/2] mm: zsmalloc: use the shrinker reclaim budget xueyuan.chen
2026-08-07 3:56 ` [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware Sergey Senozhatsky
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox