Re: [PATCH 1/2] mm/zswap: Fix global shrinker when memory cgroup is disabled

From: Yosry Ahmed

Date: Tue Jul 14 2026 - 12:55:08 EST


On Tue, Jul 14, 2026 at 1:15 AM Hao Jia <jiahao.kernel@xxxxxxxxx> wrote:
>
> From: Hao Jia <jiahao1@xxxxxxxxxxx>
>
> When memory cgroup is disabled, mem_cgroup_iter() always returns NULL.
> Therefore, the global shrinker shrink_worker() always takes the !memcg
> branch. After MAX_RECLAIM_RETRIES empty walks, the worker simply gives up,
> so it fails to write back anything.
>
> Therefore, when memory cgroup is disabled, fall through with the !memcg
> branch and shrink the root memcg directly.
>
> With memcg disabled, shrink_memcg() only returns -ENOENT when the root
> LRU is empty, which means the total pages are already below thr. The
> loop then safely bails out via the zswap_total_pages() <= thr check.
> For any other return value from shrink_memcg(), the loop is guaranteed
> to terminate, either after MAX_RECLAIM_RETRIES failures or once the
> threshold is met.
>
> Fixes: a65b0e7607cc ("zswap: make shrinking memcg-aware")
> Cc: stable@xxxxxxxxxxxxxxx
> Suggested-by: Nhat Pham <nphamcs@xxxxxxxxx>
> Acked-by: Nhat Pham <nphamcs@xxxxxxxxx>
> Acked-by: Yosry Ahmed <yosry@xxxxxxxxxx>
> Reported-by: Yosry Ahmed <yosry@xxxxxxxxxx>
> Closes: https://lore.kernel.org/all/CAO9r8zPVzMKFbCixxD-qgtRrkFxWVrHiZZeLc=eyTPKPVQgX4g@xxxxxxxxxxxxxx
> Signed-off-by: Hao Jia <jiahao1@xxxxxxxxxxx>

Patch 2 doesn't really depend on this one, right?

If that's the case I think this can (and should be) picked up
separately as a hotfix. Andrew, WDYT?

> ---
> mm/zswap.c | 11 ++++++-----
> 1 file changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/mm/zswap.c b/mm/zswap.c
> index b5a17ea20237..3d697a1a5365 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -1356,11 +1356,12 @@ static void shrink_worker(struct work_struct *w)
> } while (memcg && !mem_cgroup_tryget_online(memcg));
> spin_unlock(&zswap_shrink_lock);
>
> - if (!memcg) {
> - /*
> - * Continue shrinking without incrementing failures if
> - * we found candidate memcgs in the last tree walk.
> - */
> + /*
> + * A NULL memcg ends a full hierarchy pass (except when memcg is
> + * disabled, where it is always NULL: fall through to the root LRU).
> + * Count a failure only if the last pass found no candidates.
> + */
> + if (!memcg && !mem_cgroup_disabled()) {
> if (!attempts && ++failures == MAX_RECLAIM_RETRIES)
> break;
>
> --
> 2.34.1
>