* [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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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
2026-08-07 10:57 ` Xueyuan Chen
2 siblings, 1 reply; 14+ 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] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-07 3:56 ` [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware Sergey Senozhatsky
@ 2026-08-07 10:57 ` Xueyuan Chen
2026-08-07 12:12 ` Sergey Senozhatsky
0 siblings, 1 reply; 14+ messages in thread
From: Xueyuan Chen @ 2026-08-07 10:57 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: Minchan Kim, Andrew Morton, linux-mm, linux-kernel,
xueyuan.chen21
Hi Sergey,
Here is some additional data:
I used the following definitions:
compactable ratio = freeable_pages / total_pages
memory reclaimed = pages_freed * PAGE_SIZE
freeable_pages is the estimate before compaction, based on the same
calculation as zs_shrinker_count(), while pages_freed is the actual
number of backing pages released.
There were 264 callbacks in the trace:
callback elapsed time:
median: 5.77 ms
p95: 55.82 ms
maximum: 271.36 ms
compactable ratio before compaction:
median: 0.32%
p95: 2.86%
maximum: 8.33%
memory reclaimed per callback:
median: 3.80 MiB
p95: 30.45 MiB
maximum: 92.73 MiB
The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
and it released 7,650 pages, or about 29.88 MiB.
There was also a 241.91 ms callback (with 30 schedule-outs) with a
compactable ratio of 0.44%. It released 1,019 pages, or about
3.98 MiB.
Based on this data, it seems better to remove the shrinker.
Would you prefer that I change v2 to remove the zsmalloc shrinker
callbacks directly?
Thanks,
Xueyuan
On 8/7/2026 11:56 AM, Sergey Senozhatsky wrote:
> 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] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-07 10:57 ` Xueyuan Chen
@ 2026-08-07 12:12 ` Sergey Senozhatsky
2026-08-07 16:25 ` Nhat Pham
0 siblings, 1 reply; 14+ messages in thread
From: Sergey Senozhatsky @ 2026-08-07 12:12 UTC (permalink / raw)
To: Xueyuan Chen, Nhat Pham, Barry Song, Yosry Ahmed, Johannes Weiner,
Brian Geffon
Cc: Sergey Senozhatsky, Minchan Kim, Andrew Morton, linux-mm,
linux-kernel, xueyuan.chen21
On (26/08/07 18:57), Xueyuan Chen wrote:
> Hi Sergey,
>
> Here is some additional data:
>
> I used the following definitions:
> compactable ratio = freeable_pages / total_pages
> memory reclaimed = pages_freed * PAGE_SIZE
>
> freeable_pages is the estimate before compaction, based on the same
> calculation as zs_shrinker_count(), while pages_freed is the actual
> number of backing pages released.
>
> There were 264 callbacks in the trace:
> callback elapsed time:
> median: 5.77 ms
> p95: 55.82 ms
> maximum: 271.36 ms
>
> compactable ratio before compaction:
> median: 0.32%
> p95: 2.86%
> maximum: 8.33%
>
> memory reclaimed per callback:
> median: 3.80 MiB
> p95: 30.45 MiB
> maximum: 92.73 MiB
>
> The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
> and it released 7,650 pages, or about 29.88 MiB.
>
> There was also a 241.91 ms callback (with 30 schedule-outs) with a
> compactable ratio of 0.44%. It released 1,019 pages, or about
> 3.98 MiB.
>
> Based on this data, it seems better to remove the shrinker.
>
> Would you prefer that I change v2 to remove the zsmalloc shrinker
> callbacks directly?
Let's bring in heavy artillery to this discussion, in addition to Andrew
and Minchan, adding Nhat, Yosry, Barry, Johannes, Brian (random order).
Folks, I'm bullish on removal of zsmalloc shrinker callbacks.
I don't think those buy us much apart from memcpy-s and lock
contention. Systems that want to compact zsmalloc have a sysfs
knob (and API) to do so (based on zram mm_stat numbers).
Any thoughts?
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-07 12:12 ` Sergey Senozhatsky
@ 2026-08-07 16:25 ` Nhat Pham
2026-08-11 9:03 ` Xueyuan Chen
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Nhat Pham @ 2026-08-07 16:25 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: Xueyuan Chen, Barry Song, Yosry Ahmed, Johannes Weiner,
Brian Geffon, Minchan Kim, Andrew Morton, linux-mm, linux-kernel,
xueyuan.chen21
On Fri, Aug 7, 2026 at 5:12 AM Sergey Senozhatsky
<senozhatsky@chromium.org> wrote:
>
> On (26/08/07 18:57), Xueyuan Chen wrote:
> > Hi Sergey,
> >
> > Here is some additional data:
> >
> > I used the following definitions:
> > compactable ratio = freeable_pages / total_pages
> > memory reclaimed = pages_freed * PAGE_SIZE
> >
> > freeable_pages is the estimate before compaction, based on the same
> > calculation as zs_shrinker_count(), while pages_freed is the actual
> > number of backing pages released.
> >
> > There were 264 callbacks in the trace:
> > callback elapsed time:
> > median: 5.77 ms
> > p95: 55.82 ms
> > maximum: 271.36 ms
> >
> > compactable ratio before compaction:
> > median: 0.32%
> > p95: 2.86%
> > maximum: 8.33%
> >
> > memory reclaimed per callback:
> > median: 3.80 MiB
> > p95: 30.45 MiB
> > maximum: 92.73 MiB
> >
> > The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
> > and it released 7,650 pages, or about 29.88 MiB.
> >
> > There was also a 241.91 ms callback (with 30 schedule-outs) with a
> > compactable ratio of 0.44%. It released 1,019 pages, or about
> > 3.98 MiB.
> >
> > Based on this data, it seems better to remove the shrinker.
> >
> > Would you prefer that I change v2 to remove the zsmalloc shrinker
> > callbacks directly?
>
> Let's bring in heavy artillery to this discussion, in addition to Andrew
> and Minchan, adding Nhat, Yosry, Barry, Johannes, Brian (random order).
>
> Folks, I'm bullish on removal of zsmalloc shrinker callbacks.
> I don't think those buy us much apart from memcpy-s and lock
> contention. Systems that want to compact zsmalloc have a sysfs
> knob (and API) to do so (based on zram mm_stat numbers).
Hmm we'd need to collect more data in our workloads to determine, but
if we can compute compactable ratio (freeable_pages / total_pages) on
a per size class basis, can we just skip the size class whose ratio is
too bad? Would that at least cut down on the vast majority of
fruitless memcpys and lock acquisitions etc?
I'm always a bit hesitant to over-rely on userspace, especially when
kernel has information to do something smart about it. It might not
react in time, and many proactive reclaiming schemes back off under
heavy memory pressure.
>
> Any thoughts?
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-07 16:25 ` Nhat Pham
@ 2026-08-11 9:03 ` Xueyuan Chen
2026-08-11 16:25 ` Nhat Pham
2026-10-29 1:43 ` Sergey Senozhatsky
2026-10-29 2:26 ` Sergey Senozhatsky
2 siblings, 1 reply; 14+ messages in thread
From: Xueyuan Chen @ 2026-08-11 9:03 UTC (permalink / raw)
To: Nhat Pham, Sergey Senozhatsky
Cc: Barry Song, Yosry Ahmed, Johannes Weiner, Brian Geffon,
Minchan Kim, Andrew Morton, linux-mm, linux-kernel,
xueyuan.chen21
On 8/8/2026 12:25 AM, Nhat Pham wrote:
> On Fri, Aug 7, 2026 at 5:12 AM Sergey Senozhatsky
> <senozhatsky@chromium.org> wrote:
>> On (26/08/07 18:57), Xueyuan Chen wrote:
>>> Hi Sergey,
>>>
>>> Here is some additional data:
>>>
>>> I used the following definitions:
>>> compactable ratio = freeable_pages / total_pages
>>> memory reclaimed = pages_freed * PAGE_SIZE
>>>
>>> freeable_pages is the estimate before compaction, based on the same
>>> calculation as zs_shrinker_count(), while pages_freed is the actual
>>> number of backing pages released.
>>>
>>> There were 264 callbacks in the trace:
>>> callback elapsed time:
>>> median: 5.77 ms
>>> p95: 55.82 ms
>>> maximum: 271.36 ms
>>>
>>> compactable ratio before compaction:
>>> median: 0.32%
>>> p95: 2.86%
>>> maximum: 8.33%
>>>
>>> memory reclaimed per callback:
>>> median: 3.80 MiB
>>> p95: 30.45 MiB
>>> maximum: 92.73 MiB
>>>
>>> The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
>>> and it released 7,650 pages, or about 29.88 MiB.
>>>
>>> There was also a 241.91 ms callback (with 30 schedule-outs) with a
>>> compactable ratio of 0.44%. It released 1,019 pages, or about
>>> 3.98 MiB.
>>>
>>> Based on this data, it seems better to remove the shrinker.
>>>
>>> Would you prefer that I change v2 to remove the zsmalloc shrinker
>>> callbacks directly?
>> Let's bring in heavy artillery to this discussion, in addition to Andrew
>> and Minchan, adding Nhat, Yosry, Barry, Johannes, Brian (random order).
>>
>> Folks, I'm bullish on removal of zsmalloc shrinker callbacks.
>> I don't think those buy us much apart from memcpy-s and lock
>> contention. Systems that want to compact zsmalloc have a sysfs
>> knob (and API) to do so (based on zram mm_stat numbers).
> Hmm we'd need to collect more data in our workloads to determine, but
> if we can compute compactable ratio (freeable_pages / total_pages) on
> a per size class basis, can we just skip the size class whose ratio is
> too bad? Would that at least cut down on the vast majority of
> fruitless memcpys and lock acquisitions etc?
>
> I'm always a bit hesitant to over-rely on userspace, especially when
> kernel has information to do something smart about it. It might not
> react in time, and many proactive reclaiming schemes back off under
> heavy memory pressure.
Hi Nhat,
I collected per size-class data. 53.7% of size classes have freeable==0,
so skipping them saves some time. However, 94.7% of zs_compact() time is
spent on classes where freeable > 0 — meaning the cost is dominated by
the actual compaction work, not by scanning empty classes.
That said, skipping freeable == 0 classes is still a worthwhile
optimization on its own — it avoids unnecessary write_lock,spin_lock.
But it would not meaningfully reduce the worst-case latency.
Thanks,
Xueyuan
>> Any thoughts?
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-11 9:03 ` Xueyuan Chen
@ 2026-08-11 16:25 ` Nhat Pham
2026-08-14 16:50 ` Xueyuan Chen
2026-08-18 12:11 ` Xueyuan Chen
0 siblings, 2 replies; 14+ messages in thread
From: Nhat Pham @ 2026-08-11 16:25 UTC (permalink / raw)
To: Xueyuan Chen
Cc: Sergey Senozhatsky, Barry Song, Yosry Ahmed, Johannes Weiner,
Brian Geffon, Minchan Kim, Andrew Morton, linux-mm, linux-kernel,
xueyuan.chen21
On Tue, Aug 11, 2026 at 2:04 AM Xueyuan Chen <xueyuan.chen@vivo.com> wrote:
>
>
> On 8/8/2026 12:25 AM, Nhat Pham wrote:
> > On Fri, Aug 7, 2026 at 5:12 AM Sergey Senozhatsky
> > <senozhatsky@chromium.org> wrote:
> >> On (26/08/07 18:57), Xueyuan Chen wrote:
> >>> Hi Sergey,
> >>>
> >>> Here is some additional data:
> >>>
> >>> I used the following definitions:
> >>> compactable ratio = freeable_pages / total_pages
> >>> memory reclaimed = pages_freed * PAGE_SIZE
> >>>
> >>> freeable_pages is the estimate before compaction, based on the same
> >>> calculation as zs_shrinker_count(), while pages_freed is the actual
> >>> number of backing pages released.
> >>>
> >>> There were 264 callbacks in the trace:
> >>> callback elapsed time:
> >>> median: 5.77 ms
> >>> p95: 55.82 ms
> >>> maximum: 271.36 ms
> >>>
> >>> compactable ratio before compaction:
> >>> median: 0.32%
> >>> p95: 2.86%
> >>> maximum: 8.33%
> >>>
> >>> memory reclaimed per callback:
> >>> median: 3.80 MiB
> >>> p95: 30.45 MiB
> >>> maximum: 92.73 MiB
> >>>
> >>> The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
> >>> and it released 7,650 pages, or about 29.88 MiB.
> >>>
> >>> There was also a 241.91 ms callback (with 30 schedule-outs) with a
> >>> compactable ratio of 0.44%. It released 1,019 pages, or about
> >>> 3.98 MiB.
> >>>
> >>> Based on this data, it seems better to remove the shrinker.
> >>>
> >>> Would you prefer that I change v2 to remove the zsmalloc shrinker
> >>> callbacks directly?
> >> Let's bring in heavy artillery to this discussion, in addition to Andrew
> >> and Minchan, adding Nhat, Yosry, Barry, Johannes, Brian (random order).
> >>
> >> Folks, I'm bullish on removal of zsmalloc shrinker callbacks.
> >> I don't think those buy us much apart from memcpy-s and lock
> >> contention. Systems that want to compact zsmalloc have a sysfs
> >> knob (and API) to do so (based on zram mm_stat numbers).
> > Hmm we'd need to collect more data in our workloads to determine, but
> > if we can compute compactable ratio (freeable_pages / total_pages) on
> > a per size class basis, can we just skip the size class whose ratio is
> > too bad? Would that at least cut down on the vast majority of
> > fruitless memcpys and lock acquisitions etc?
> >
> > I'm always a bit hesitant to over-rely on userspace, especially when
> > kernel has information to do something smart about it. It might not
> > react in time, and many proactive reclaiming schemes back off under
> > heavy memory pressure.
>
> Hi Nhat,
>
> I collected per size-class data. 53.7% of size classes have freeable==0,
> so skipping them saves some time. However, 94.7% of zs_compact() time is
> spent on classes where freeable > 0 — meaning the cost is dominated by
> the actual compaction work, not by scanning empty classes.
What's the distribution of these? Say if I were to skip all size class
with compactable ratio below 20%, or those where we can free at least
one full zspage? (IIUC, we only free any memory at all if a zspage got
freed up from our internal compaction, correct?).
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-11 16:25 ` Nhat Pham
@ 2026-08-14 16:50 ` Xueyuan Chen
2026-08-18 12:11 ` Xueyuan Chen
1 sibling, 0 replies; 14+ messages in thread
From: Xueyuan Chen @ 2026-08-14 16:50 UTC (permalink / raw)
To: Nhat Pham
Cc: Xueyuan Chen, Sergey Senozhatsky, Barry Song, Yosry Ahmed,
Johannes Weiner, Brian Geffon, Minchan Kim, Andrew Morton,
linux-mm, linux-kernel
On Wed, Aug 12, 2026 at 12:25 AM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Tue, Aug 11, 2026 at 2:04 AM Xueyuan Chen <xueyuan.chen@vivo.com> wrote:
> >
> >
> > On 8/8/2026 12:25 AM, Nhat Pham wrote:
> > > On Fri, Aug 7, 2026 at 5:12 AM Sergey Senozhatsky
> > > <senozhatsky@chromium.org> wrote:
> > >> On (26/08/07 18:57), Xueyuan Chen wrote:
> > >>> Hi Sergey,
> > >>>
> > >>> Here is some additional data:
> > >>>
> > >>> I used the following definitions:
> > >>> compactable ratio = freeable_pages / total_pages
> > >>> memory reclaimed = pages_freed * PAGE_SIZE
> > >>>
> > >>> freeable_pages is the estimate before compaction, based on the same
> > >>> calculation as zs_shrinker_count(), while pages_freed is the actual
> > >>> number of backing pages released.
> > >>>
> > >>> There were 264 callbacks in the trace:
> > >>> callback elapsed time:
> > >>> median: 5.77 ms
> > >>> p95: 55.82 ms
> > >>> maximum: 271.36 ms
> > >>>
> > >>> compactable ratio before compaction:
> > >>> median: 0.32%
> > >>> p95: 2.86%
> > >>> maximum: 8.33%
> > >>>
> > >>> memory reclaimed per callback:
> > >>> median: 3.80 MiB
> > >>> p95: 30.45 MiB
> > >>> maximum: 92.73 MiB
> > >>>
> > >>> The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
> > >>> and it released 7,650 pages, or about 29.88 MiB.
> > >>>
> > >>> There was also a 241.91 ms callback (with 30 schedule-outs) with a
> > >>> compactable ratio of 0.44%. It released 1,019 pages, or about
> > >>> 3.98 MiB.
> > >>>
> > >>> Based on this data, it seems better to remove the shrinker.
> > >>>
> > >>> Would you prefer that I change v2 to remove the zsmalloc shrinker
> > >>> callbacks directly?
> > >> Let's bring in heavy artillery to this discussion, in addition to Andrew
> > >> and Minchan, adding Nhat, Yosry, Barry, Johannes, Brian (random order).
> > >>
> > >> Folks, I'm bullish on removal of zsmalloc shrinker callbacks.
> > >> I don't think those buy us much apart from memcpy-s and lock
> > >> contention. Systems that want to compact zsmalloc have a sysfs
> > >> knob (and API) to do so (based on zram mm_stat numbers).
> > > Hmm we'd need to collect more data in our workloads to determine, but
> > > if we can compute compactable ratio (freeable_pages / total_pages) on
> > > a per size class basis, can we just skip the size class whose ratio is
> > > too bad? Would that at least cut down on the vast majority of
> > > fruitless memcpys and lock acquisitions etc?
> > >
> > > I'm always a bit hesitant to over-rely on userspace, especially when
> > > kernel has information to do something smart about it. It might not
> > > react in time, and many proactive reclaiming schemes back off under
> > > heavy memory pressure.
> >
> > Hi Nhat,
> >
> > I collected per size-class data. 53.7% of size classes have freeable==0,
> > so skipping them saves some time. However, 94.7% of zs_compact() time is
> > spent on classes where freeable > 0 — meaning the cost is dominated by
> > the actual compaction work, not by scanning empty classes.
>
Hi Nhat,
Sorry for the late reply.
> What's the distribution of these? Say if I were to skip all size class
> with compactable ratio below 20%, or those where we can free at least
> one full zspage? (IIUC, we only free any memory at all if a zspage got
> freed up from our internal compaction, correct?).
You are right, we only free the backing pages when a full zspage is emptied
by compaction.
My test device had some problems, I need some time to collect new data.
The good news is the per-size-class distribution should be ready next week.
Thanks
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-11 16:25 ` Nhat Pham
2026-08-14 16:50 ` Xueyuan Chen
@ 2026-08-18 12:11 ` Xueyuan Chen
1 sibling, 0 replies; 14+ messages in thread
From: Xueyuan Chen @ 2026-08-18 12:11 UTC (permalink / raw)
To: Nhat Pham
Cc: Sergey Senozhatsky, Barry Song, Yosry Ahmed, Johannes Weiner,
Brian Geffon, Minchan Kim, Andrew Morton, linux-mm, linux-kernel,
xueyuan.chen21
On 8/12/2026 12:25 AM, Nhat Pham wrote:
> On Tue, Aug 11, 2026 at 2:04 AM Xueyuan Chen <xueyuan.chen@vivo.com> wrote:
>>
>> On 8/8/2026 12:25 AM, Nhat Pham wrote:
>>> On Fri, Aug 7, 2026 at 5:12 AM Sergey Senozhatsky
>>> <senozhatsky@chromium.org> wrote:
>>>> On (26/08/07 18:57), Xueyuan Chen wrote:
>>>>> Hi Sergey,
>>>>>
>>>>> Here is some additional data:
>>>>>
>>>>> I used the following definitions:
>>>>> compactable ratio = freeable_pages / total_pages
>>>>> memory reclaimed = pages_freed * PAGE_SIZE
>>>>>
>>>>> freeable_pages is the estimate before compaction, based on the same
>>>>> calculation as zs_shrinker_count(), while pages_freed is the actual
>>>>> number of backing pages released.
>>>>>
>>>>> There were 264 callbacks in the trace:
>>>>> callback elapsed time:
>>>>> median: 5.77 ms
>>>>> p95: 55.82 ms
>>>>> maximum: 271.36 ms
>>>>>
>>>>> compactable ratio before compaction:
>>>>> median: 0.32%
>>>>> p95: 2.86%
>>>>> maximum: 8.33%
>>>>>
>>>>> memory reclaimed per callback:
>>>>> median: 3.80 MiB
>>>>> p95: 30.45 MiB
>>>>> maximum: 92.73 MiB
>>>>>
>>>>> The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
>>>>> and it released 7,650 pages, or about 29.88 MiB.
>>>>>
>>>>> There was also a 241.91 ms callback (with 30 schedule-outs) with a
>>>>> compactable ratio of 0.44%. It released 1,019 pages, or about
>>>>> 3.98 MiB.
>>>>>
>>>>> Based on this data, it seems better to remove the shrinker.
>>>>>
>>>>> Would you prefer that I change v2 to remove the zsmalloc shrinker
>>>>> callbacks directly?
>>>> Let's bring in heavy artillery to this discussion, in addition to Andrew
>>>> and Minchan, adding Nhat, Yosry, Barry, Johannes, Brian (random order).
>>>>
>>>> Folks, I'm bullish on removal of zsmalloc shrinker callbacks.
>>>> I don't think those buy us much apart from memcpy-s and lock
>>>> contention. Systems that want to compact zsmalloc have a sysfs
>>>> knob (and API) to do so (based on zram mm_stat numbers).
>>> Hmm we'd need to collect more data in our workloads to determine, but
>>> if we can compute compactable ratio (freeable_pages / total_pages) on
>>> a per size class basis, can we just skip the size class whose ratio is
>>> too bad? Would that at least cut down on the vast majority of
>>> fruitless memcpys and lock acquisitions etc?
>>>
>>> I'm always a bit hesitant to over-rely on userspace, especially when
>>> kernel has information to do something smart about it. It might not
>>> react in time, and many proactive reclaiming schemes back off under
>>> heavy memory pressure.
>> Hi Nhat,
>>
>> I collected per size-class data. 53.7% of size classes have freeable==0,
>> so skipping them saves some time. However, 94.7% of zs_compact() time is
>> spent on classes where freeable > 0 — meaning the cost is dominated by
>> the actual compaction work, not by scanning empty classes.
> What's the distribution of these? Say if I were to skip all size class
> with compactable ratio below 20%, or those where we can free at least
> one full zspage? (IIUC, we only free any memory at all if a zspage got
> freed up from our internal compaction, correct?).
Hi Nhat,
Across 106 zram0 compaction callbacks, there were 6,246 per-class
events with freeable > 0, covering 118 size classes.
The data by object size was:
| object size (bytes) | classes | events | total pages | freeable pages |
| [0, 256) | 14 | 570 | 182297 | 6728 |
| [256, 512) | 16 | 748 | 480127 | 14526 |
| [512, 1024) | 30 | 1586 | 2677075 | 71934 |
| [1024, 2048) | 36 | 2180 | 11990170 | 294133 |
| [2048, 3072) | 15 | 821 | 5089301 | 106204 |
| [3072, 4096] | 7 | 341 | 1170843 | 20012 |
The compactable-ratio distribution at class-compaction entry was:
p25: 0.75%
median: 1.65%
p90: 6.76%
p95: 9.42%
p99: 14.52%
maximum: 30.99%
Only 12 out of 6,246 events had a ratio of 20% or higher, so a 20%
threshold would skip nearly all of them.
| freeable pages | total pages | compactable ratio |
class A | 10 | 20 | 50% |
class B | 20 | 50 | 40% |
In this example, class B has more freeable pages even though its ratio
is lower. So I don't think the ratio alone is enough to decide whether
to compact a class.
Thanks,
Xueyuan
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-07 16:25 ` Nhat Pham
2026-08-11 9:03 ` Xueyuan Chen
@ 2026-10-29 1:43 ` Sergey Senozhatsky
2026-10-29 1:48 ` Sergey Senozhatsky
2026-10-29 2:26 ` Sergey Senozhatsky
2 siblings, 1 reply; 14+ messages in thread
From: Sergey Senozhatsky @ 2026-10-29 1:43 UTC (permalink / raw)
To: Nhat Pham
Cc: Sergey Senozhatsky, Xueyuan Chen, Barry Song, Yosry Ahmed,
Johannes Weiner, Brian Geffon, Minchan Kim, Andrew Morton,
linux-mm, linux-kernel, xueyuan.chen21
On (26/08/07 09:25), Nhat Pham wrote:
> > On (26/08/07 18:57), Xueyuan Chen wrote:
> > > Hi Sergey,
> > >
> > > Here is some additional data:
> > >
> > > I used the following definitions:
> > > compactable ratio = freeable_pages / total_pages
> > > memory reclaimed = pages_freed * PAGE_SIZE
> > >
> > > freeable_pages is the estimate before compaction, based on the same
> > > calculation as zs_shrinker_count(), while pages_freed is the actual
> > > number of backing pages released.
> > >
> > > There were 264 callbacks in the trace:
> > > callback elapsed time:
> > > median: 5.77 ms
> > > p95: 55.82 ms
> > > maximum: 271.36 ms
> > >
> > > compactable ratio before compaction:
> > > median: 0.32%
> > > p95: 2.86%
> > > maximum: 8.33%
> > >
> > > memory reclaimed per callback:
> > > median: 3.80 MiB
> > > p95: 30.45 MiB
> > > maximum: 92.73 MiB
> > >
> > > The longest callback took 271.36 ms. Its compactable ratio was 3.25%,
> > > and it released 7,650 pages, or about 29.88 MiB.
> > >
> > > There was also a 241.91 ms callback (with 30 schedule-outs) with a
> > > compactable ratio of 0.44%. It released 1,019 pages, or about
> > > 3.98 MiB.
> > >
> > > Based on this data, it seems better to remove the shrinker.
> > >
> > > Would you prefer that I change v2 to remove the zsmalloc shrinker
> > > callbacks directly?
> >
> > Let's bring in heavy artillery to this discussion, in addition to Andrew
> > and Minchan, adding Nhat, Yosry, Barry, Johannes, Brian (random order).
> >
> > Folks, I'm bullish on removal of zsmalloc shrinker callbacks.
> > I don't think those buy us much apart from memcpy-s and lock
> > contention. Systems that want to compact zsmalloc have a sysfs
> > knob (and API) to do so (based on zram mm_stat numbers).
>
> Hmm we'd need to collect more data in our workloads to determine, but
> if we can compute compactable ratio (freeable_pages / total_pages) on
> a per size class basis, can we just skip the size class whose ratio is
> too bad? Would that at least cut down on the vast majority of
> fruitless memcpys and lock acquisitions etc?
>
> I'm always a bit hesitant to over-rely on userspace, especially when
> kernel has information to do something smart about it. It might not
> react in time, and many proactive reclaiming schemes back off under
> heavy memory pressure.
Apologies for the delay.
The thing about shrinker compaction is that it's sort of on the
wrong end. It doesn't trigger compaction when memory is plentiful,
no matter the internal fragmentation rate, and start firing compaction
scans when memory pressure hits. The problem with this is that, now
compaction moves objects (if can compact zspages) and releases empty
zspages, in parallel with potentially heavy swapping out activities,
which can simply allocate those released pages back. Which seems like
a useless exercise and needless memcpy-s. So I was thinking about
either putting it under config or just removing it (this might be a
harder decision).
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-10-29 1:43 ` Sergey Senozhatsky
@ 2026-10-29 1:48 ` Sergey Senozhatsky
0 siblings, 0 replies; 14+ messages in thread
From: Sergey Senozhatsky @ 2026-10-29 1:48 UTC (permalink / raw)
To: Nhat Pham
Cc: Xueyuan Chen, Barry Song, Yosry Ahmed, Johannes Weiner,
Brian Geffon, Minchan Kim, Andrew Morton, linux-mm, linux-kernel,
xueyuan.chen21, Sergey Senozhatsky
On (26/10/29 10:43), Sergey Senozhatsky wrote:
[..]
> > Hmm we'd need to collect more data in our workloads to determine, but
> > if we can compute compactable ratio (freeable_pages / total_pages) on
> > a per size class basis, can we just skip the size class whose ratio is
> > too bad? Would that at least cut down on the vast majority of
> > fruitless memcpys and lock acquisitions etc?
> >
> > I'm always a bit hesitant to over-rely on userspace, especially when
> > kernel has information to do something smart about it. It might not
> > react in time, and many proactive reclaiming schemes back off under
> > heavy memory pressure.
>
> Apologies for the delay.
>
> The thing about shrinker compaction is that it's sort of on the
> wrong end. It doesn't trigger compaction when memory is plentiful,
> no matter the internal fragmentation rate, and start firing compaction
> scans when memory pressure hits. The problem with this is that, now
> compaction moves objects (if can compact zspages) and releases empty
> zspages, in parallel with potentially heavy swapping out activities,
> which can simply allocate those released pages back. Which seems like
> a useless exercise and needless memcpy-s. So I was thinking about
> either putting it under config or just removing it (this might be a
> harder decision).
In embedded (android/bookOS/etc.) there is a daemon, that triggers
various zram activities. We can wire in pool-compaction (once under
memory pressure), which is a zram sysfs device attribute, instead
of pondering zsmalloc with scan callbacks.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 0/2] mm: zsmalloc: make shrinker compaction budget-aware
2026-08-07 16:25 ` Nhat Pham
2026-08-11 9:03 ` Xueyuan Chen
2026-10-29 1:43 ` Sergey Senozhatsky
@ 2026-10-29 2:26 ` Sergey Senozhatsky
2 siblings, 0 replies; 14+ messages in thread
From: Sergey Senozhatsky @ 2026-10-29 2:26 UTC (permalink / raw)
To: Nhat Pham
Cc: Sergey Senozhatsky, Xueyuan Chen, Barry Song, Yosry Ahmed,
Johannes Weiner, Brian Geffon, Minchan Kim, Andrew Morton,
linux-mm, linux-kernel, xueyuan.chen21
On (26/08/07 09:25), Nhat Pham wrote:
> Hmm we'd need to collect more data in our workloads to determine, but
> if we can compute compactable ratio (freeable_pages / total_pages) on
> a per size class basis, can we just skip the size class whose ratio is
> too bad? Would that at least cut down on the vast majority of
> fruitless memcpys and lock acquisitions etc?
To calculate those we need to tool pool/class locks. But technically
possible. Not sure, how would be determine the threshold tho.
On the other alternative is quite simple. We can make it
"default ZSWAP", for example.
---
diff --git a/mm/Kconfig b/mm/Kconfig
index afff1b25b543..72542946bc53 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -157,6 +157,17 @@ config ZSMALLOC_CHAIN_SIZE
For more information, see zsmalloc documentation.
+config ZSMALLOC_SHRINKER
+ bool "Enable zsmalloc pool shrinker"
+ default y
+ help
+ This option enables the zsmalloc pool shrinker, which allows
+ the zsmalloc pool to be shrunk when the system is under memory
+ pressure. The shrinker will attempt to free up memory by
+ compacting zspages and releasing empty zspages back to the system.
+
+ If unsure, say N.
+
endmenu
menu "Slab allocator options"
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 7ef80e0da626..e0d7ab9142bb 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -276,8 +276,10 @@ struct zs_pool {
struct zs_pool_stats stats;
+#ifdef CONFIG_ZSMALLOC_SHRINKER
/* Compact classes */
struct shrinker *shrinker;
+#endif
#ifdef CONFIG_ZSMALLOC_STAT
struct dentry *stat_dentry;
@@ -2050,8 +2052,9 @@ void zs_pool_stats(struct zs_pool *pool, struct zs_pool_stats *stats)
}
EXPORT_SYMBOL_GPL(zs_pool_stats);
+#ifdef CONFIG_ZSMALLOC_SHRINKER
static unsigned long zs_shrinker_scan(struct shrinker *shrinker,
- struct shrink_control *sc)
+ struct shrink_control *sc)
{
unsigned long pages_freed;
struct zs_pool *pool = shrinker->private_data;
@@ -2067,7 +2070,7 @@ static unsigned long zs_shrinker_scan(struct shrinker *shrinker,
}
static unsigned long zs_shrinker_count(struct shrinker *shrinker,
- struct shrink_control *sc)
+ struct shrink_control *sc)
{
int i;
struct size_class *class;
@@ -2105,6 +2108,10 @@ static int zs_register_shrinker(struct zs_pool *pool)
return 0;
}
+#else
+static void zs_unregister_shrinker(struct zs_pool *pool) { }
+static int zs_register_shrinker(struct zs_pool *pool) { return 0; }
+#endif
static int calculate_zspage_chain_size(int class_size)
{
^ permalink raw reply related [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-10-29 2:26 UTC | newest]
Thread overview: 14+ 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
2026-08-07 10:57 ` Xueyuan Chen
2026-08-07 12:12 ` Sergey Senozhatsky
2026-08-07 16:25 ` Nhat Pham
2026-08-11 9:03 ` Xueyuan Chen
2026-08-11 16:25 ` Nhat Pham
2026-08-14 16:50 ` Xueyuan Chen
2026-08-18 12:11 ` Xueyuan Chen
2026-10-29 1:43 ` Sergey Senozhatsky
2026-10-29 1:48 ` Sergey Senozhatsky
2026-10-29 2:26 ` Sergey Senozhatsky
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox