mm: zswap: add per-memcg stat for incompressible pages
Subsystems (4)
PATCH 1/1 mm: zswap: add per-memcg stat for incompressible pages
From: Jiayuan Chen <[email protected]>
The global zswap_stored_incompressible_pages counter was added in commit dca4437a5861 ("mm/zswap: store <PAGE_SIZE compression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.
Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped * PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.
Changes
-------
1. Add zswap_is_raw() helper (include/linux/zswap.h)
- Abstract the PAGE_SIZE comparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability
2. Add MEMCG_ZSWAP_RAW stat definition (include/linux/memcontrol.h, mm/memcontrol.c)
- Add MEMCG_ZSWAP_RAW to memcg_stat_item enum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat
3. Update statistics accounting (mm/memcontrol.c, mm/zswap.c)
- Track MEMCG_ZSWAP_RAW in obj_cgroup_charge/uncharge_zswap()
- Use zswap_is_raw() helper in zswap.c for consistency
Test
----
I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0
$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0
$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0
[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
--- include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)
include/linux/memcontrol.h +1 -0
@@ -39,6 +39,7 @@ enum memcg_stat_item {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , +MEMCG_ZSWAP_RAW , MEMCG_NR_STAT, };
include/linux/zswap.h +9 -0
@@ -7,6 +7,15 @@struct lruvec ; +/* +* Check if a zswap entry is stored in raw (uncompressed) form. +* This happens when compression doesn't reduce the size. +*/ +static inline bool zswap_is_raw (size_t size) +{ +return size ==PAGE_SIZE ; +} +extern atomic_long_t zswap_stored_pages;#ifdef CONFIG_ZSWAP
mm/memcontrol.c +6 -0
@@ -341,6 +341,7 @@ static const unsigned int memcg_stat_items[] = {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW, };#define NR_MEMCG_NODE_STAT_ITEMS ARRAY_SIZE(memcg_node_stat_items)
@@ -1346,6 +1347,7 @@ static const struct memory_stat memory_stats[] = { #ifdef CONFIG_ZSWAP { "zswap", MEMCG_ZSWAP_B }, { "zswapped", MEMCG_ZSWAPPED }, + { "zswpraw", MEMCG_ZSWAP_RAW }, #endif { "file_mapped", NR_FILE_MAPPED }, { "file_dirty", NR_FILE_DIRTY },
@@ -5458,6 +5460,8 @@ void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,1 );rcu_read_unlock (); }
@@ -5481,6 +5485,8 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , -size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,-1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,-1 );rcu_read_unlock (); }
mm/zswap.c +3 -3
@@ -723,7 +723,7 @@ static void zswap_entry_free(struct zswap_entry *entry)obj_cgroup_uncharge_zswap (entry->objcg, entry->length);obj_cgroup_put (entry->objcg); } -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_dec (&zswap_stored_incompressible_pages);zswap_entry_cache_free (entry);atomic_long_dec (&zswap_stored_pages);
@@ -941,7 +941,7 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)zs_obj_read_sg_begin (pool->zs_pool, entry->handle, input, entry->length);/* zswap entries of length PAGE_SIZE are not compressed. */ -if (entry->length ==PAGE_SIZE ) { +if (zswap_is_raw (entry->length)) {WARN_ON_ONCE (input->length !=PAGE_SIZE );memcpy_from_sglist (kmap_local_folio (folio,0 ), input,0 ,PAGE_SIZE ); dlen =PAGE_SIZE ;
@@ -1448,7 +1448,7 @@ static bool zswap_store_page(struct page *page,obj_cgroup_charge_zswap (objcg, entry->length); }atomic_long_inc (&zswap_stored_pages); -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_inc (&zswap_stored_incompressible_pages);/*
Signature · 1 line
2.43.0
Quoted 21 lines · Jiayuan Chen
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
Quoted 8 lines
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.
I personally agree. This is especially useful for multi-tenants setups, where different workloads can have different compressibility, which can muddy the waters, and might prefer different swapping treatment (disk swapping, zswapping, zswap + disk swap through zswap shrinker). It might also give us data to extend zswap (zswap compressibility-based rejection, or different compression levels).
Naming is a bit off though, but I'm not a native English speaker :) I think Chris Li pointed the out the necessity of per-memcg counters too:
https://lore.kernel.org/linux-mm/CAF8kJuONDFj4NAksaR4j_WyDbNwNGYLmTe-o76rqU17La=nkOw@mail.gmail.com/
Can you add this to the patch changelog in later versions? :)
+ Chris Li
Quoted 39 lines
Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0
Quoted 8 lines
$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Would be nice if some versions of this can be turned into a selftest :)
Instead of reading zstd data, you can read from /dev/urandom. I found those to be incompressible, usually.
Feel free to send this as a follow-up patch, but would love to see it :)
Quoted 46 lines
Signed-off-by Jiayuan Chen <[email protected]>--- include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)include/linux/memcontrol.h +1
-0@@ -39,6 +39,7 @@ enum memcg_stat_item {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , +MEMCG_ZSWAP_RAW , MEMCG_NR_STAT, };include/linux/zswap.h +9
-0@@ -7,6 +7,15 @@struct lruvec ; +/* +* Check if a zswap entry is stored in raw (uncompressed) form. +* This happens when compression doesn't reduce the size. +*/ +static inline bool zswap_is_raw (size_t size) +{ +return size ==PAGE_SIZE ; +} +extern atomic_long_t zswap_stored_pages;#ifdef CONFIG_ZSWAP mm/memcontrol.c +0
-0
mm/memcontrol.c static const unsigned int memcg_stat_items[] = { · line 344
Quoted 4 lines
mm/memcontrol.c +1
-0@@ -341,6 +341,7 @@ static const unsigned int memcg_stat_items[] = {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW,
not sure how I feel about the naming, but I don't have a recommendation :)
Quoted 61 lines
mm/memcontrol.c +5
-0};#define NR_MEMCG_NODE_STAT_ITEMS ARRAY_SIZE(memcg_node_stat_items)@@ -1346,6 +1347,7 @@ static const struct memory_stat memory_stats[] = { #ifdef CONFIG_ZSWAP { "zswap", MEMCG_ZSWAP_B }, { "zswapped", MEMCG_ZSWAPPED }, + { "zswpraw", MEMCG_ZSWAP_RAW }, #endif { "file_mapped", NR_FILE_MAPPED }, { "file_dirty", NR_FILE_DIRTY },@@ -5458,6 +5460,8 @@ void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,1 );rcu_read_unlock (); }@@ -5481,6 +5485,8 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , -size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,-1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,-1 );rcu_read_unlock (); }mm/zswap.c +3
-4@@ -723,7 +723,7 @@ static void zswap_entry_free(struct zswap_entry *entry)obj_cgroup_uncharge_zswap (entry->objcg, entry->length);obj_cgroup_put (entry->objcg); } -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_dec (&zswap_stored_incompressible_pages);zswap_entry_cache_free (entry);atomic_long_dec (&zswap_stored_pages);@@ -941,7 +941,7 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)zs_obj_read_sg_begin (pool->zs_pool, entry->handle, input, entry->length);/* zswap entries of length PAGE_SIZE are not compressed. */ -if (entry->length ==PAGE_SIZE ) { +if (zswap_is_raw (entry->length)) {WARN_ON_ONCE (input->length !=PAGE_SIZE );memcpy_from_sglist (kmap_local_folio (folio,0 ), input,0 ,PAGE_SIZE ); dlen =PAGE_SIZE ;@@ -1448,7 +1448,7 @@ static bool zswap_store_page(struct page *page,obj_cgroup_charge_zswap (objcg, entry->length); }atomic_long_inc (&zswap_stored_pages); -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_inc (&zswap_stored_incompressible_pages);/* --
Quoted 2 lines
2.43.0
Those nits aside, LGTM.
I'll leave the naming suggestion to Yosry and Johannes ;)
Nhat Pham
maintainerQuoted 23 lines · Nhat Pham
Quoted 21 lines · Jiayuan Chen
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
Quoted 9 lines
Quoted 8 lines
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.
Actually I forgot - can you also update the Documentation:
Documentation/admin-guide/cgroup-v2.rst
to include a short description of the new counter? Thanks!
Quoted 35 lines · Nhat Pham
Quoted 23 lines · Nhat Pham
On Wed, Feb 4, 2026 at 9:31 PM Jiayuan Chen <[email protected]> wrote:
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
Quoted 9 lines
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.
Quoted 6 lines
Actually I forgot - can you also update the Documentation:
Documentation/admin-guide/cgroup-v2.rstto include a short description of the new counter? Thanks!
Thanks Nhat for the review and Acked-by!
I'll update the documentation and add Chris Li's link to changelog in v2. Will consider the selftest as a follow-up.
Shakeel Butt
maintainerQuoted 74 lines · Jiayuan Chen
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0
Quoted 3 lines
[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Signed-off-by Jiayuan Chen <[email protected]>
Overall looks good but as Nhat suggested please update v2 documentation.
Quoted 12 lines
--- include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)include/linux/memcontrol.h +0
-0
include/linux/memcontrol.h enum memcg_stat_item { · line 42
Quoted 4 lines
include/linux/memcontrol.h +1
-0@@ -39,6 +39,7 @@ enum memcg_stat_item {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW,
Hmm I don't like the name though. How about INCOMPRESSIBLE or INCOMP for short?
Quoted 103 lines · Shakeel Butt
Quoted 75 lines · Jiayuan Chen
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0Quoted 4 lines
[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Signed-off-by Jiayuan Chen <[email protected]>Overall looks good but as Nhat suggested please update v2 documentation.
Quoted 8 lines
--- include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)Quoted 10 lines
diff --git
a/include/linux/memcontrol.hb/include/linux/memcontrol.hindexb6c82c8f73e1..83d1328f81d1100644
---a/include/linux/memcontrol.h
+++b/include/linux/memcontrol.h
@@ -39,6 +39,7 @@ enummemcg_stat_item{
MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,
Quoted 3 lines
Hmm I don't like the name though. How about INCOMPRESSIBLE or INCOMP for short?
Thanks Shakeel! I'll rename it to INCOMP and update the documentation in v2.
Quoted 89 lines · Jiayuan Chen
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Signed-off-by Jiayuan Chen <[email protected]>--- include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)include/linux/memcontrol.h +0
-0
include/linux/memcontrol.h enum memcg_stat_item { · line 42
Quoted 4 lines
include/linux/memcontrol.h +1
-0@@ -39,6 +39,7 @@ enum memcg_stat_item {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW,
Please change the name as Shakeel suggested.
Quoted 8 lines
include/linux/memcontrol.h +0
-0MEMCG_NR_STAT, };include/linux/zswap.h +0
-0
include/linux/zswap.h line 14
Quoted 8 lines
include/linux/zswap.h +5
-0@@ -7,6 +7,15 @@struct lruvec ; +/* +* Check if a zswap entry is stored in raw (uncompressed) form. +* This happens when compression doesn't reduce the size. +*/ +static inline bool zswap_is_raw (size_t size)
Internall as well, please rename this to zswap_is_incompressible() or zswap_is_incomp(). Not a big fan of the helper because it doesn't add much, but I don't feel strongly either way.
Quoted 20 lines
include/linux/zswap.h +4
-0+{ +return size ==PAGE_SIZE ; +} +extern atomic_long_t zswap_stored_pages;#ifdef CONFIG_ZSWAP mm/memcontrol.c +1
-0@@ -341,6 +341,7 @@ static const unsigned int memcg_stat_items[] = {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW, };#define NR_MEMCG_NODE_STAT_ITEMS ARRAY_SIZE(memcg_node_stat_items)
mm/memcontrol.c static const struct memory_stat memory_stats[] = { · line 1350
Quoted 4 lines
mm/memcontrol.c +1
-0@@ -1346,6 +1347,7 @@ static const struct memory_stat memory_stats[] = { #ifdef CONFIG_ZSWAP { "zswap", MEMCG_ZSWAP_B }, { "zswapped", MEMCG_ZSWAPPED }, + { "zswpraw", MEMCG_ZSWAP_RAW },
Here as well: zswap_incompressible or zswap_incomp?
Other than the renames and doc, LGTM.
Quoted 55 lines
mm/memcontrol.c +4
-0#endif { "file_mapped", NR_FILE_MAPPED }, { "file_dirty", NR_FILE_DIRTY },@@ -5458,6 +5460,8 @@ void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,1 );rcu_read_unlock (); }@@ -5481,6 +5485,8 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , -size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,-1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,-1 );rcu_read_unlock (); }mm/zswap.c +3
-3@@ -723,7 +723,7 @@ static void zswap_entry_free(struct zswap_entry *entry)obj_cgroup_uncharge_zswap (entry->objcg, entry->length);obj_cgroup_put (entry->objcg); } -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_dec (&zswap_stored_incompressible_pages);zswap_entry_cache_free (entry);atomic_long_dec (&zswap_stored_pages);@@ -941,7 +941,7 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)zs_obj_read_sg_begin (pool->zs_pool, entry->handle, input, entry->length);/* zswap entries of length PAGE_SIZE are not compressed. */ -if (entry->length ==PAGE_SIZE ) { +if (zswap_is_raw (entry->length)) {WARN_ON_ONCE (input->length !=PAGE_SIZE );memcpy_from_sglist (kmap_local_folio (folio,0 ), input,0 ,PAGE_SIZE ); dlen =PAGE_SIZE ;@@ -1448,7 +1448,7 @@ static bool zswap_store_page(struct page *page,obj_cgroup_charge_zswap (objcg, entry->length); }atomic_long_inc (&zswap_stored_pages); -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_inc (&zswap_stored_incompressible_pages);/* Signature · 1 line
2.43.0
Quoted 148 lines · Yosry Ahmed
Quoted 85 lines · Jiayuan Chen
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Signed-off-by Jiayuan Chen <[email protected]>---
include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)Quoted 10 lines
diff --git
a/include/linux/memcontrol.hb/include/linux/memcontrol.hindexb6c82c8f73e1..83d1328f81d1100644
---a/include/linux/memcontrol.h
+++b/include/linux/memcontrol.h
@@ -39,6 +39,7 @@ enummemcg_stat_item{
MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,Please change the name as Shakeel suggested.
Quoted 10 lines
MEMCG_NR_STAT,
};diff --git
a/include/linux/zswap.hb/include/linux/zswap.hindex30c193a1207e..94f84b154b71100644
---a/include/linux/zswap.h
+++b/include/linux/zswap.h
@@ -7,6 +7,15 @@Quoted 8 lines
struct lruvec;
+/*
+ * Check if a zswap entry is stored in raw (uncompressed) form.
+ * This happens when compression doesn't reduce the size.
+ */
+static inline boolzswap_is_raw(size_t size)Internall as well, please rename this to
zswap_is_incompressible()orzswap_is_incomp(). Not a big fan of the helper because it doesn't add much, but I don't feel strongly either way.Quoted 19 lines
+{
+ return size ==PAGE_SIZE;
+}
+
externatomic_long_tzswap_stored_pages;#ifdef
CONFIG_ZSWAP
diff --gita/mm/memcontrol.cb/mm/memcontrol.c
index007413a53b45..32fb801530a3100644
---a/mm/memcontrol.c
+++b/mm/memcontrol.c
@@ -341,6 +341,7 @@ static const unsigned int memcg_stat_items[] = {MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,
};Quoted 7 lines
#define
NR_MEMCG_NODE_STAT_ITEMSARRAY_SIZE(memcg_node_stat_items)@@ -1346,6 +1347,7 @@ static const structmemory_statmemory_stats[] = { #ifdefCONFIG_ZSWAP
{ "zswap",MEMCG_ZSWAP_B},
{ "zswapped",MEMCG_ZSWAPPED},
+ { "zswpraw",MEMCG_ZSWAP_RAW},
Quoted 5 lines
Here as well:
zswap_incompressibleorzswap_incomp?Other than the renames and doc, LGTM.
Quoted 1 line
Thanks Yosry!
I'll rename everything to use "incomp" (MEMCG_ZSWAP_INCOMP, zswap_is_incomp(), "zswap_incomp") and update the documentation in v2.
Quoted 76 lines · On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <[email protected]> wrote:
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Quoted 7 lines
Signed-off-by Jiayuan Chen <[email protected]>--- include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)
As others also mentioned, the documentation of the new stat would be needed.
Quoted 20 lines
include/linux/memcontrol.h +1
-0@@ -39,6 +39,7 @@ enum memcg_stat_item {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , +MEMCG_ZSWAP_RAW , MEMCG_NR_STAT, };include/linux/zswap.h +0
-0@@ -7,6 +7,15 @@struct lruvec ;
include/linux/zswap.h line 18
Quoted 10 lines
include/linux/zswap.h +9
-0+/* +* Check if a zswap entry is stored in raw (uncompressed) form. +* This happens when compression doesn't reduce the size. +*/ +static inline bool zswap_is_raw (size_t size) +{ +return size ==PAGE_SIZE ; +} +
No strong opinion, but I'm not really sure if the helper is needed, because it feels quite simple logic:
"If an object is compressed and the size is same to the original one, the object is incompressible."
I also feel the function name bit odd, given the type of the parameter. Based on the function name and the comment, I'd expect it to receive a zswap_entry object. I understand it is better to receive a size_t, to be called from obj_cgroup_[un]charge_zswap(), though. Even in the case, I think the name can be better (e.g., zswap_compression_failed() or zswap_was_incompressible() ?), or at least the coment can be more kindly explain the fact that the parameter is the size of object after the compression attempt.
mm/memcontrol.c static const unsigned int memcg_stat_items[] = { · line 345
Quoted 13 lines
include/linux/zswap.h +0
-0extern atomic_long_t zswap_stored_pages;#ifdef CONFIG_ZSWAP mm/memcontrol.c +1
-0@@ -341,6 +341,7 @@ static const unsigned int memcg_stat_items[] = {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW, };
No strong opinion, but I think Shakeel's suggestion of other names is reasonable.
mm/memcontrol.c static const struct memory_stat memory_stats[] = { · line 1350
Quoted 7 lines
mm/memcontrol.c +1
-0#define NR_MEMCG_NODE_STAT_ITEMS ARRAY_SIZE(memcg_node_stat_items)@@ -1346,6 +1347,7 @@ static const struct memory_stat memory_stats[] = { #ifdef CONFIG_ZSWAP { "zswap", MEMCG_ZSWAP_B }, { "zswapped", MEMCG_ZSWAPPED }, + { "zswpraw", MEMCG_ZSWAP_RAW },
Ditto.
mm/memcontrol.c void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size) · line 5464
Quoted 9 lines
mm/memcontrol.c +2
-0#endif { "file_mapped", NR_FILE_MAPPED }, { "file_dirty", NR_FILE_DIRTY },@@ -5458,6 +5460,8 @@ void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,1 );
I understand the helper function is better to receive size_t rather than zswap_entry for this.
Quoted 26 lines
mm/memcontrol.c +2
-0rcu_read_unlock (); }@@ -5481,6 +5485,8 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size) memcg =obj_cgroup_memcg (objcg);mod_memcg_state (memcg,MEMCG_ZSWAP_B , -size);mod_memcg_state (memcg,MEMCG_ZSWAPPED ,-1 ); +if (zswap_is_raw (size)) +mod_memcg_state (memcg,MEMCG_ZSWAP_RAW ,-1 );rcu_read_unlock (); }mm/zswap.c +1
-1@@ -723,7 +723,7 @@ static void zswap_entry_free(struct zswap_entry *entry)obj_cgroup_uncharge_zswap (entry->objcg, entry->length);obj_cgroup_put (entry->objcg); } -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_dec (&zswap_stored_incompressible_pages);zswap_entry_cache_free (entry);atomic_long_dec (&zswap_stored_pages);
mm/zswap.c static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio) · line 947
Quoted 8 lines
mm/zswap.c +1
-1@@ -941,7 +941,7 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)zs_obj_read_sg_begin (pool->zs_pool, entry->handle, input, entry->length);/* zswap entries of length PAGE_SIZE are not compressed. */ -if (entry->length ==PAGE_SIZE ) { +if (zswap_is_raw (entry->length)) {WARN_ON_ONCE (input->length !=PAGE_SIZE );memcpy_from_sglist (kmap_local_folio (folio,0 ), input,0 ,PAGE_SIZE ); dlen =PAGE_SIZE ;
Below this part, I show 'dlen == PAGE_SIZE'. Should it also be converted to use the helper function?
Quoted 11 lines
mm/zswap.c +1
-1@@ -1448,7 +1448,7 @@ static bool zswap_store_page(struct page *page,obj_cgroup_charge_zswap (objcg, entry->length); }atomic_long_inc (&zswap_stored_pages); -if (entry->length ==PAGE_SIZE ) +if (zswap_is_raw (entry->length))atomic_long_inc (&zswap_stored_incompressible_pages);/* Signature · 1 line
2.43.0
Looks good to me overall, though. Thank you for this patch.
Thanks,
SJ
Quoted 127 lines · On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <> wrote:
Quoted 78 lines
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Quoted 8 lines
Signed-off-by Jiayuan Chen <[email protected]>---
include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)As others also mentioned, the documentation of the new stat would be needed.
Quoted 21 lines
include/linux/memcontrol.h +0
-0index b6c82c8f73e1..83d1328f81d1 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -39,6 +39,7 @@ enum memcg_stat_item {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW,MEMCG_NR_STAT , }; diff --git a/include/linux/zswap.h b/include/linux/zswap.h index 30c193a1207e..94f84b154b71 100644 --- a/include/linux/zswap.h +++ b/include/linux/zswap.h @@ -7,6 +7,15 @@struct lruvec ;include/linux/memcontrol.h
Quoted 10 lines
include/linux/memcontrol.h +0
-0+/* + * Check if a zswap entry is stored in raw (uncompressed) form. + * This happens when compression doesn't reduce the size. + */ +static inline bool zswap_is_raw(size_t size) +{ + return size == PAGE_SIZE; +} +No strong opinion, but I'm not really sure if the helper is needed, because it feels quite simple logic:
"If an object is compressed and the size is same to the original one, the object is incompressible."
Quoted 7 lines
I also feel the function name bit odd, given the type of the parameter. Based on the function name and the comment, I'd expect it to receive a
zswap_entryobject. I understand it is better to receive asize_t, to be called from obj_cgroup_[un]charge_zswap(), though. Even in the case, I think the name can be better (e.g.,zswap_compression_failed()orzswap_was_incompressible()?), or at least the coment can be more kindly explain the fact that the parameter is the size of object after the compression attempt.
I vote to drop the helper.
Quoted 136 lines · Yosry Ahmed
Quoted 128 lines · On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <> wrote:
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Signed-off-by Jiayuan Chen <[email protected]>---
include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)As others also mentioned, the documentation of the new stat would be needed.
diff --git
a/include/linux/memcontrol.hb/include/linux/memcontrol.hindexb6c82c8f73e1..83d1328f81d1100644
---a/include/linux/memcontrol.h
+++b/include/linux/memcontrol.h
@@ -39,6 +39,7 @@ enummemcg_stat_item{
MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,
MEMCG_NR_STAT,
};diff --git
a/include/linux/zswap.hb/include/linux/zswap.hindex30c193a1207e..94f84b154b71100644
---a/include/linux/zswap.h
+++b/include/linux/zswap.h
@@ -7,6 +7,15 @@struct lruvec;
+/*
+ * Check if a zswap entry is stored in raw (uncompressed) form.
+ * This happens when compression doesn't reduce the size.
+ */
+static inline boolzswap_is_raw(size_t size)
+{
+ return size ==PAGE_SIZE;
+}
+No strong opinion, but I'm not really sure if the helper is needed, because it feels quite simple logic:
"If an object is compressed and the size is same to the original one, the object is incompressible."
Quoted 8 lines
I also feel the function name bit odd, given the type of the parameter. Based on the function name and the comment, I'd expect it to receive a
zswap_entryobject. I understand it is better to receive asize_t, to be called from obj_cgroup_[un]charge_zswap(), though. Even in the case, I think the name can be better (e.g.,zswap_compression_failed()orzswap_was_incompressible()?), or at least the coment can be more kindly explain the fact that the parameter is the size of object after the compression attempt.
Quoted 2 lines
I vote to drop the helper.
The reason I introduced the helper is that the incompressible check now lives in two places:
In zswap.c - for the global zswap_stored_incompressible_pages counter In memcontrol.c - for the per-memcg MEMCG_ZSWAP_INCOMP stat
By extracting a shared helper, both modules use the same logic, which helps with maintainability.
That said, I'm fine with dropping the helper if preferred. I can add a comment in memcontrol.c explaining the logic. My only concern is that if the incompressible detection logic in zswap ever changes, someone might forget to update the memcg accounting accordingly.
But perhaps that's an unlikely scenario.
Quoted 142 lines
Quoted 135 lines · On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <> wrote:
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Signed-off-by Jiayuan Chen <[email protected]>---
include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)As others also mentioned, the documentation of the new stat would be needed.
diff --git
a/include/linux/memcontrol.hb/include/linux/memcontrol.hindexb6c82c8f73e1..83d1328f81d1100644
---a/include/linux/memcontrol.h
+++b/include/linux/memcontrol.h
@@ -39,6 +39,7 @@ enummemcg_stat_item{
MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,
MEMCG_NR_STAT,
};diff --git
a/include/linux/zswap.hb/include/linux/zswap.hindex30c193a1207e..94f84b154b71100644
---a/include/linux/zswap.h
+++b/include/linux/zswap.h
@@ -7,6 +7,15 @@struct lruvec;
+/*
+ * Check if a zswap entry is stored in raw (uncompressed) form.
+ * This happens when compression doesn't reduce the size.
+ */
+static inline boolzswap_is_raw(size_t size)
+{
+ return size ==PAGE_SIZE;
+}
+No strong opinion, but I'm not really sure if the helper is needed, because it feels quite simple logic:
"If an object is compressed and the size is same to the original one, the object is incompressible."
I also feel the function name bit odd, given the type of the parameter. Based on the function name and the comment, I'd expect it to receive a
zswap_entryobject. I understand it is better to receive asize_t, to be called from obj_cgroup_[un]charge_zswap(), though. Even in the case, I think the name can be better (e.g.,zswap_compression_failed()orzswap_was_incompressible()?), or at least the coment can be more kindly explain the fact that the parameter is the size of object after the compression attempt.Quoted 2 lines
I vote to drop the helper.
The reason I introduced the helper is that the incompressible check now lives in two places:
In
zswap.c- for the globalzswap_stored_incompressible_pagescounter Inmemcontrol.c- for the per-memcgMEMCG_ZSWAP_INCOMPstat
Quoted 7 lines
By extracting a shared helper, both modules use the same logic, which helps with maintainability.
That said, I'm fine with dropping the helper if preferred. I can add a comment in
memcontrol.cexplaining the logic. My only concern is that if the incompressible detection logic in zswap ever changes, someone might forget to update the memcg accounting accordingly.But perhaps that's an unlikely scenario.
Well, a selftest would be the right way to detect such a problem imo. Even if we need to have a customer definition for incompressible later, it should remain in zswap and we should pass it into memcg code.
For now, I think let's keep open-coding the PAGE_SIZE check.
Quoted 213 lines · SeongJae Park
Quoted 77 lines · On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <[email protected]> wrote:
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Quoted 8 lines
Signed-off-by Jiayuan Chen <[email protected]>---
include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)As others also mentioned, the documentation of the new stat would be needed.
Quoted 21 lines
include/linux/memcontrol.h +0
-0index b6c82c8f73e1..83d1328f81d1 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -39,6 +39,7 @@ enum memcg_stat_item {MEMCG_KMEM ,MEMCG_ZSWAP_B ,MEMCG_ZSWAPPED , + MEMCG_ZSWAP_RAW,MEMCG_NR_STAT , }; diff --git a/include/linux/zswap.h b/include/linux/zswap.h index 30c193a1207e..94f84b154b71 100644 --- a/include/linux/zswap.h +++ b/include/linux/zswap.h @@ -7,6 +7,15 @@struct lruvec ;include/linux/memcontrol.h
Quoted 10 lines
include/linux/memcontrol.h +0
-0+/* + * Check if a zswap entry is stored in raw (uncompressed) form. + * This happens when compression doesn't reduce the size. + */ +static inline bool zswap_is_raw(size_t size) +{ + return size == PAGE_SIZE; +} +No strong opinion, but I'm not really sure if the helper is needed, because it feels quite simple logic:
"If an object is compressed and the size is same to the original one, the object is incompressible."
I also feel the function name bit odd, given the type of the parameter. Based on the function name and the comment, I'd expect it to receive a
zswap_entryobject. I understand it is better to receive asize_t, to be called from obj_cgroup_[un]charge_zswap(), though. Even in the case, I think the name can be better (e.g.,zswap_compression_failed()orzswap_was_incompressible()?), or at least the coment can be more kindly explain the fact that the parameter is the size of object after the compression attempt.Quoted 15 lines
extern
atomic_long_tzswap_stored_pages;#ifdef
CONFIG_ZSWAP
diff --gita/mm/memcontrol.cb/mm/memcontrol.c
index007413a53b45..32fb801530a3100644
---a/mm/memcontrol.c
+++b/mm/memcontrol.c
@@ -341,6 +341,7 @@ static const unsigned int memcg_stat_items[] = {MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,
};No strong opinion, but I think Shakeel's suggestion of other names is reasonable.
Quoted 8 lines
#define
NR_MEMCG_NODE_STAT_ITEMSARRAY_SIZE(memcg_node_stat_items)
@@ -1346,6 +1347,7 @@ static const structmemory_statmemory_stats[] = { #ifdefCONFIG_ZSWAP
{ "zswap",MEMCG_ZSWAP_B},
{ "zswapped",MEMCG_ZSWAPPED},
+ { "zswpraw",MEMCG_ZSWAP_RAW},Ditto.
Quoted 11 lines
#endif
{ "file_mapped",NR_FILE_MAPPED},
{ "file_dirty",NR_FILE_DIRTY},
@@ -5458,6 +5460,8 @@ voidobj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size)memcg =obj_cgroup_memcg(objcg);
mod_memcg_state(memcg, MEMCG_ZSWAP_B, size);
mod_memcg_state(memcg, MEMCG_ZSWAPPED, 1);
+ if (zswap_is_raw(size))
+mod_memcg_state(memcg, MEMCG_ZSWAP_RAW, 1);I understand the helper function is better to receive
size_trather thanzswap_entryfor this.Quoted 29 lines
rcu_read_unlock();
}@@ -5481,6 +5485,8 @@ void
obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size)memcg =obj_cgroup_memcg(objcg);
mod_memcg_state(memcg, MEMCG_ZSWAP_B, -size);
mod_memcg_state(memcg, MEMCG_ZSWAPPED, -1);
+ if (zswap_is_raw(size))
+mod_memcg_state(memcg, MEMCG_ZSWAP_RAW, -1);
rcu_read_unlock();
}diff --git
a/mm/zswap.cb/mm/zswap.c
index3d2d59ac3f9c..54ab4d126f64100644
---a/mm/zswap.c
+++b/mm/zswap.c
@@ -723,7 +723,7 @@ static voidzswap_entry_free(struct zswap_entry *entry)obj_cgroup_uncharge_zswap(entry->objcg, entry->length);obj_cgroup_put(entry->objcg);
}
- if (entry->length==PAGE_SIZE)
+ if (zswap_is_raw(entry->length))atomic_long_dec(&zswap_stored_incompressible_pages);zswap_entry_cache_free(entry);
atomic_long_dec(&zswap_stored_pages);
@@ -941,7 +941,7 @@ static boolzswap_decompress(struct zswap_entry *entry, struct folio *folio)zs_obj_read_sg_begin(pool->zs_pool, entry->handle, input, entry->length);Quoted 7 lines
/* zswap entries of length
PAGE_SIZEare not compressed. */
- if (entry->length==PAGE_SIZE) {
+ if (zswap_is_raw(entry->length)) {WARN_ON_ONCE(input->length != PAGE_SIZE);memcpy_from_sglist(kmap_local_folio(folio, 0), input, 0, PAGE_SIZE); dlen =PAGE_SIZE;
Quoted 3 lines
Below this part, I show 'dlen ==
PAGE_SIZE'. Should it also be converted to use the helper function?
The dlen variable represents the decompressed (plaintext) size. Since we compress individual pages, the decompressed output should always be PAGE_SIZE in normal cases.
This check validates whether decompression produced the expected result, not whether the entry is incompressible.
Using zswap_is_incomp() here would be semantically incorrect - the helper is meant to check if an entry was stored without compression (i.e., compression failed to reduce size), while dlen == PAGE_SIZE verifies the output of decompression is valid.
Quoted 5 lines · On Fri, 06 Feb 2026 02:47:58 +0000 "Jiayuan Chen" <[email protected]>…
Quoted 2 lines · SeongJae Park
On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <[email protected]> wrote:
[...]
Quoted 18 lines
Quoted 10 lines
Quoted 10 lines
@@ -941,7 +941,7 @@ static bool
zswap_decompress(struct zswap_entry *entry, struct folio *folio)zs_obj_read_sg_begin(pool->zs_pool, entry->handle, input, entry->length);/* zswap entries of length
PAGE_SIZEare not compressed. */
- if (entry->length=PAGE_SIZE) {
+ if (zswap_is_raw(entry->length)) {WARN_ON_ONCE(input->length != PAGE_SIZE);memcpy_from_sglist(kmap_local_folio(folio, 0), input, 0, PAGE_SIZE); dlen =PAGE_SIZE;Quoted 3 lines
Below this part, I show 'dlen =
PAGE_SIZE'. Should it also be converted to use the helper function?The dlen variable represents the decompressed (plaintext) size. Since we compress individual pages, the decompressed output should always be
PAGE_SIZEin normal cases.
Quoted 5 lines
This check validates whether decompression produced the expected result, not whether the entry is incompressible.
Using
zswap_is_incomp()here would be semantically incorrect - the helper is meant to check if an entry was stored without compression (i.e., compression failed to reduce size), while dlen =PAGE_SIZEverifies the output of decompression is valid.
You are right. Thank you for kindly correcting me.
Thanks,
SJ
Quoted 218 lines · On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <[email protected]> wrote:
Quoted 211 lines
From: Jiayuan Chen <[email protected]>
The global
zswap_stored_incompressible_pagescounter was added in commitdca4437a5861("mm/zswap: store <PAGE_SIZEcompression failed page as-is") to track how many pages are stored in raw (uncompressed) form in zswap. However, in containerized environments, knowing which cgroup is contributing incompressible pages is essential for effective resource management.Add a new memcg stat 'zswpraw' to track incompressible pages per cgroup. This helps administrators and orchestrators to:
1. Identify workloads that produce incompressible data (e.g., encrypted data, already-compressed media, random data) and may not benefit from zswap.
2. Make informed decisions about workload placement - moving incompressible workloads to nodes with larger swap backing devices rather than relying on zswap.
3. Debug zswap efficiency issues at the cgroup level without needing to correlate global stats with individual cgroups.
While the compression ratio can be estimated from existing stats (zswap / zswapped *
PAGE_SIZE), this doesn't distinguish between "uniformly poor compression" and "a few completely incompressible pages mixed with highly compressible ones". The zswpraw stat provides direct visibility into the latter case.Changes
-------1. Add
zswap_is_raw()helper (include/linux/zswap.h)
- Abstract thePAGE_SIZEcomparison logic for identifying raw entries
- Keep the incompressible check in one place for maintainability2. Add
MEMCG_ZSWAP_RAWstat definition (include/linux/memcontrol.h,mm/memcontrol.c)
- AddMEMCG_ZSWAP_RAWtomemcg_stat_itemenum
- Register in memcg_stat_items[] and memory_stats[] arrays
- Export as "zswpraw" in memory.stat3. Update statistics accounting (
mm/memcontrol.c,mm/zswap.c)
- TrackMEMCG_ZSWAP_RAWin obj_cgroup_charge/uncharge_zswap()
- Usezswap_is_raw()helper inzswap.cfor consistencyTest
----I wrote a simple test program[1] that allocates memory and compresses it with zstd, so kernel zswap cannot compress further.
$ cgcreate -g memory:test $ cgexec -g memory:test ./test_zswpraw & $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 0 zswpout 0 zswpwb 0$ echo "100M" > /sys/fs/cgroup/test/memory.reclaim $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 104800256 zswpin 0 zswpout 51222 zswpwb 0$ pkill test_zswpraw $ cat /sys/fs/cgroup/test/memory.stat | grep zswp zswpraw 0 zswpin 1 zswpout 51222 zswpwb 0[1] https://gist.github.com/mrpre/00432c6154250326994fbeaf62e0e6f1
Signed-off-by Jiayuan Chen <[email protected]>---
include/linux/memcontrol.h | 1 + include/linux/zswap.h | 9 +++++++++ mm/memcontrol.c | 6 ++++++ mm/zswap.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-)As others also mentioned, the documentation of the new stat would be needed.
diff --git
a/include/linux/memcontrol.hb/include/linux/memcontrol.hindexb6c82c8f73e1..83d1328f81d1100644
---a/include/linux/memcontrol.h
+++b/include/linux/memcontrol.h
@@ -39,6 +39,7 @@ enummemcg_stat_item{
MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,
MEMCG_NR_STAT,
};diff --git
a/include/linux/zswap.hb/include/linux/zswap.hindex30c193a1207e..94f84b154b71100644
---a/include/linux/zswap.h
+++b/include/linux/zswap.h
@@ -7,6 +7,15 @@struct lruvec;
+/*
+ * Check if a zswap entry is stored in raw (uncompressed) form.
+ * This happens when compression doesn't reduce the size.
+ */
+static inline boolzswap_is_raw(size_t size)
+{
+ return size ==PAGE_SIZE;
+}
+No strong opinion, but I'm not really sure if the helper is needed, because it feels quite simple logic:
"If an object is compressed and the size is same to the original one, the object is incompressible."
I also feel the function name bit odd, given the type of the parameter. Based on the function name and the comment, I'd expect it to receive a
zswap_entryobject. I understand it is better to receive asize_t, to be called from obj_cgroup_[un]charge_zswap(), though. Even in the case, I think the name can be better (e.g.,zswap_compression_failed()orzswap_was_incompressible()?), or at least the coment can be more kindly explain the fact that the parameter is the size of object after the compression attempt.extern
atomic_long_tzswap_stored_pages;#ifdef
CONFIG_ZSWAP
diff --gita/mm/memcontrol.cb/mm/memcontrol.c
index007413a53b45..32fb801530a3100644
---a/mm/memcontrol.c
+++b/mm/memcontrol.c
@@ -341,6 +341,7 @@ static const unsigned int memcg_stat_items[] = {MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
+MEMCG_ZSWAP_RAW,
};No strong opinion, but I think Shakeel's suggestion of other names is reasonable.
#define
NR_MEMCG_NODE_STAT_ITEMSARRAY_SIZE(memcg_node_stat_items)@@ -1346,6 +1347,7 @@ static const structmemory_statmemory_stats[] = { #ifdefCONFIG_ZSWAP
{ "zswap",MEMCG_ZSWAP_B},
{ "zswapped",MEMCG_ZSWAPPED},
+ { "zswpraw",MEMCG_ZSWAP_RAW},Ditto.
#endif
{ "file_mapped",NR_FILE_MAPPED},
{ "file_dirty",NR_FILE_DIRTY},
@@ -5458,6 +5460,8 @@ voidobj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size)memcg =obj_cgroup_memcg(objcg);
mod_memcg_state(memcg, MEMCG_ZSWAP_B, size);
mod_memcg_state(memcg, MEMCG_ZSWAPPED, 1);
+ if (zswap_is_raw(size))
+mod_memcg_state(memcg, MEMCG_ZSWAP_RAW, 1);I understand the helper function is better to receive
size_trather thanzswap_entryfor this.
rcu_read_unlock();
}@@ -5481,6 +5485,8 @@ void
obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size)memcg =obj_cgroup_memcg(objcg);
mod_memcg_state(memcg, MEMCG_ZSWAP_B, -size);
mod_memcg_state(memcg, MEMCG_ZSWAPPED, -1);
+ if (zswap_is_raw(size))
+mod_memcg_state(memcg, MEMCG_ZSWAP_RAW, -1);
rcu_read_unlock();
}diff --git
a/mm/zswap.cb/mm/zswap.c
index3d2d59ac3f9c..54ab4d126f64100644
---a/mm/zswap.c
+++b/mm/zswap.c
@@ -723,7 +723,7 @@ static voidzswap_entry_free(struct zswap_entry *entry)obj_cgroup_uncharge_zswap(entry->objcg, entry->length);obj_cgroup_put(entry->objcg);
}
- if (entry->length==PAGE_SIZE)
+ if (zswap_is_raw(entry->length))atomic_long_dec(&zswap_stored_incompressible_pages);zswap_entry_cache_free(entry);
atomic_long_dec(&zswap_stored_pages);
@@ -941,7 +941,7 @@ static boolzswap_decompress(struct zswap_entry *entry, struct folio *folio)zs_obj_read_sg_begin(pool->zs_pool, entry->handle, input, entry->length);/* zswap entries of length
PAGE_SIZEare not compressed. */
- if (entry->length==PAGE_SIZE) {
+ if (zswap_is_raw(entry->length)) {WARN_ON_ONCE(input->length != PAGE_SIZE);memcpy_from_sglist(kmap_local_folio(folio, 0), input, 0, PAGE_SIZE); dlen =PAGE_SIZE;Quoted 3 lines
Below this part, I show 'dlen ==
PAGE_SIZE'. Should it also be converted to use the helper function?The dlen variable represents the decompressed (plaintext) size. Since we compress individual pages, the decompressed output should always be
PAGE_SIZEin normal cases.
Quoted 5 lines
This check validates whether decompression produced the expected result, not whether the entry is incompressible.
Using
zswap_is_incomp()here would be semantically incorrect - the helper is meant to check if an entry was stored without compression (i.e., compression failed to reduce size), while dlen ==PAGE_SIZEverifies the output of decompression is valid.
This confusion is exactly why we should drop the helper.