[PATCH v1] mm: zswap: add per-memcg stat for incompressible pages

Jiayuan Chen posted 1 patch 2 days, 6 hours ago
There is a newer version of this series
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(-)
[PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Jiayuan Chen 2 days, 6 hours ago
From: Jiayuan Chen <jiayuan.chen@shopee.com>

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

Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
---
 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(-)

diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 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;
 
+/*
+ * 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
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 007413a53b45..32fb801530a3 100644
--- 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,
 };
 
 #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();
 }
 
diff --git a/mm/zswap.c b/mm/zswap.c
index 3d2d59ac3f9c..54ab4d126f64 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -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);
 
 	/*
-- 
2.43.0
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by SeongJae Park 1 day, 9 hours ago
On Thu,  5 Feb 2026 13:30:12 +0800 Jiayuan Chen <jiayuan.chen@linux.dev> wrote:

> From: Jiayuan Chen <jiayuan.chen@shopee.com>
> 
> 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
> 
> Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> ---
>  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.h b/include/linux/memcontrol.h
> index 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;
>  
> +/*
> + * 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.

>  extern atomic_long_t zswap_stored_pages;
>  
>  #ifdef CONFIG_ZSWAP
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 007413a53b45..32fb801530a3 100644
> --- 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_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.

>  #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.

>  	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.c b/mm/zswap.c
> index 3d2d59ac3f9c..54ab4d126f64 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -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;

Below this part, I show 'dlen == PAGE_SIZE'.  Should it also be converted to
use the helper function?

> @@ -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);
>  
>  	/*
> -- 
> 2.43.0

Looks good to me overall, though.  Thank you for this patch.


Thanks,
SJ
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Jiayuan Chen 1 day, 9 hours ago
February 6, 2026 at 10:21, "SeongJae Park" <sj@kernel.org mailto:sj@kernel.org?to=%22SeongJae%20Park%22%20%3Csj%40kernel.org%3E > wrote:


> 
> On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
> 
> > 
> > From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  
> >  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
> >  
> >  Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  ---
> >  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.h b/include/linux/memcontrol.h
> >  index 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;
> >  
> >  +/*
> >  + * 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.
> 
> > 
> > extern atomic_long_t zswap_stored_pages;
> >  
> >  #ifdef CONFIG_ZSWAP
> >  diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> >  index 007413a53b45..32fb801530a3 100644
> >  --- 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_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.
> 
> > 
> > #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.
> 
> > 
> > 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.c b/mm/zswap.c
> >  index 3d2d59ac3f9c..54ab4d126f64 100644
> >  --- a/mm/zswap.c
> >  +++ b/mm/zswap.c
> >  @@ -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;
> > 
> 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.
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Yosry Ahmed 1 day, 7 hours ago
On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
> >  
> >  
> >  From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  
> >  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
> >  
> >  Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  ---
> >  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.h b/include/linux/memcontrol.h
> >  index 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;
> >  
> >  +/*
> >  + * 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.
> >  
> >  
> >  extern atomic_long_t zswap_stored_pages;
> >  
> >  #ifdef CONFIG_ZSWAP
> >  diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> >  index 007413a53b45..32fb801530a3 100644
> >  --- 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_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.
> >  
> >  
> >  #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.
> >  
> >  
> >  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.c b/mm/zswap.c
> >  index 3d2d59ac3f9c..54ab4d126f64 100644
> >  --- a/mm/zswap.c
> >  +++ b/mm/zswap.c
> >  @@ -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;
> >  
> >  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.


This confusion is exactly why we should drop the helper.
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by SeongJae Park 1 day, 8 hours ago
On Fri, 06 Feb 2026 02:47:58 +0000 "Jiayuan Chen" <jiayuan.chen@linux.dev> wrote:

> February 6, 2026 at 10:21, "SeongJae Park" <sj@kernel.org mailto:sj@kernel.org?to=%22SeongJae%20Park%22%20%3Csj%40kernel.org%3E > wrote:
> 
> 
> > 
> > On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
[...]
> > >  @@ -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?
> > 
> 
> 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.

You are right.  Thank you for kindly correcting me.


Thanks,
SJ
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Yosry Ahmed 1 day, 9 hours ago
> 
> On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <> wrote:
> 
> > 
> > From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  
> >  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
> >  
> >  Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  ---
> >  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.h b/include/linux/memcontrol.h
> >  index 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;
> >  
> >  +/*
> >  + * 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.

I vote to drop the helper.
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Jiayuan Chen 1 day, 8 hours ago
February 6, 2026 at 10:33, "Yosry Ahmed" <yosry.ahmed@linux.dev mailto:yosry.ahmed@linux.dev?to=%22Yosry%20Ahmed%22%20%3Cyosry.ahmed%40linux.dev%3E > wrote:


> 
> > 
> > On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <> wrote:
> >  
> >  
> >  From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  
> >  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
> >  
> >  Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  ---
> >  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.h b/include/linux/memcontrol.h
> >  index 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;
> >  
> >  +/*
> >  + * 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.
> > 
> 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.
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Yosry Ahmed 1 day, 7 hours ago
> > 
> > On Thu, 5 Feb 2026 13:30:12 +0800 Jiayuan Chen <> wrote:
> >  
> >  
> >  From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  
> >  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
> >  
> >  Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  ---
> >  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.h b/include/linux/memcontrol.h
> >  index 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;
> >  
> >  +/*
> >  + * 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.
> >  
> >  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.

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.
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Yosry Ahmed 1 day, 11 hours ago
On Thu, Feb 05, 2026 at 01:30:12PM +0800, Jiayuan Chen wrote:
> From: Jiayuan Chen <jiayuan.chen@shopee.com>
> 
> 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
> 
> Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> ---
>  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(-)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 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,

Please change the name as Shakeel suggested.

>  	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;
>  
> +/*
> + * 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.

> +{
> +	return size == PAGE_SIZE;
> +}
> +
>  extern atomic_long_t zswap_stored_pages;
>  
>  #ifdef CONFIG_ZSWAP
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 007413a53b45..32fb801530a3 100644
> --- 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,
>  };
>  
>  #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			},

Here as well: zswap_incompressible or zswap_incomp?

Other than the renames and doc, LGTM.

>  #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();
>  }
>  
> diff --git a/mm/zswap.c b/mm/zswap.c
> index 3d2d59ac3f9c..54ab4d126f64 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -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);
>  
>  	/*
> -- 
> 2.43.0
>
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Jiayuan Chen 1 day, 9 hours ago
February 6, 2026 at 08:39, "Yosry Ahmed" <yosry.ahmed@linux.dev mailto:yosry.ahmed@linux.dev?to=%22Yosry%20Ahmed%22%20%3Cyosry.ahmed%40linux.dev%3E > wrote:


> 
> On Thu, Feb 05, 2026 at 01:30:12PM +0800, Jiayuan Chen wrote:
> 
> > 
> > From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  
> >  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
> >  
> >  Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  ---
> >  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(-)
> >  
> >  diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> >  index 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,
> > 
> Please change the name as Shakeel suggested.
> 
> > 
> > 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;
> >  
> >  +/*
> >  + * 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.
> 
> > 
> > +{
> >  + return size == PAGE_SIZE;
> >  +}
> >  +
> >  extern atomic_long_t zswap_stored_pages;
> >  
> >  #ifdef CONFIG_ZSWAP
> >  diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> >  index 007413a53b45..32fb801530a3 100644
> >  --- 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,
> >  };
> >  
> >  #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 },
> > 
> Here as well: zswap_incompressible or zswap_incomp?
> 
> Other than the renames and doc, LGTM.
> 
> > 


Thanks Yosry!

I'll rename everything to use "incomp" (MEMCG_ZSWAP_INCOMP, zswap_is_incomp(), "zswap_incomp")
and update the documentation in v2.
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Shakeel Butt 1 day, 14 hours ago
On Thu, Feb 05, 2026 at 01:30:12PM +0800, Jiayuan Chen wrote:
> From: Jiayuan Chen <jiayuan.chen@shopee.com>
> 
> 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
> 
> Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>

Overall looks good but as Nhat suggested please update v2
documentation.


> ---
>  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(-)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 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,

Hmm I don't like the name though. How about INCOMPRESSIBLE or INCOMP for
short?
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Jiayuan Chen 1 day, 9 hours ago
February 6, 2026 at 05:33, "Shakeel Butt" <shakeel.butt@linux.dev mailto:shakeel.butt@linux.dev?to=%22Shakeel%20Butt%22%20%3Cshakeel.butt%40linux.dev%3E > wrote:


> 
> On Thu, Feb 05, 2026 at 01:30:12PM +0800, Jiayuan Chen wrote:
> 
> > 
> > From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >  
> >  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
> >  
> >  Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> > 
> Overall looks good but as Nhat suggested please update v2
> documentation.
> 
> > 
> > ---
> >  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(-)
> >  
> >  diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> >  index 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,
> > 
> 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.
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Nhat Pham 1 day, 18 hours ago
On Wed, Feb 4, 2026 at 9:31 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
> From: Jiayuan Chen <jiayuan.chen@shopee.com>
>
> 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.

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
>
> 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

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 :)

>
> Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> ---
>  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(-)
>
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 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;
>
> +/*
> + * 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
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 007413a53b45..32fb801530a3 100644
> --- 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,

not sure how I feel about the naming, but I don't have a recommendation :)

>  };
>
>  #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();
>  }
>
> diff --git a/mm/zswap.c b/mm/zswap.c
> index 3d2d59ac3f9c..54ab4d126f64 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -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);
>
>         /*
> --
> 2.43.0
>

Those nits aside, LGTM.
Acked-by: Nhat Pham <nphamcs@gmail.com>

I'll leave the naming suggestion to Yosry and Johannes ;)
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Nhat Pham 1 day, 18 hours ago
On Thu, Feb 5, 2026 at 9:31 AM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Wed, Feb 4, 2026 at 9:31 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
> >
> > From: Jiayuan Chen <jiayuan.chen@shopee.com>
> >
> > 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.
>

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!
Re: [PATCH v1] mm: zswap: add per-memcg stat for incompressible pages
Posted by Jiayuan Chen 1 day, 9 hours ago
February 6, 2026 at 01:45, "Nhat Pham" <nphamcs@gmail.com mailto:nphamcs@gmail.com?to=%22Nhat%20Pham%22%20%3Cnphamcs%40gmail.com%3E > wrote:


> 
> On Thu, Feb 5, 2026 at 9:31 AM Nhat Pham <nphamcs@gmail.com> wrote:
> 
> > 
> > On Wed, Feb 4, 2026 at 9:31 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
> > 
> >  From: Jiayuan Chen <jiayuan.chen@shopee.com>
> > 
> >  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.
> > 
> 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!
>

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.