All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jiayuan Chen" <jiayuan.chen@linux.dev>, linux-mm@kvack.org
Cc: "Johannes Weiner" <hannes@cmpxchg.org>,
	"Michal Hocko" <mhocko@kernel.org>,
	"Roman Gushchin" <roman.gushchin@linux.dev>,
	"Shakeel Butt" <shakeel.butt@linux.dev>,
	"Muchun Song" <muchun.song@linux.dev>,
	"Yosry Ahmed" <yosry@kernel.org>, "Nhat Pham" <nphamcs@gmail.com>,
	"Chengming Zhou" <chengming.zhou@linux.dev>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Chris Li" <chrisl@kernel.org>,
	"Kairui Song" <kasong@tencent.com>,
	"Kemeng Shi" <shikemeng@huaweicloud.com>,
	"Baoquan He" <baoquan.he@linux.dev>,
	"Barry Song" <baohua@kernel.org>,
	"Youngjun Park" <youngjun.park@lge.com>,
	"David Hildenbrand" <david@kernel.org>,
	"Qi Zheng" <qi.zheng@linux.dev>,
	"Lorenzo Stoakes" <ljs@kernel.org>,
	"Axel Rasmussen" <axelrasmussen@google.com>,
	"Yuanchu Xie" <yuanchu@google.com>, "Wei Xu" <weixugc@google.com>,
	"Baolin Wang" <baolin.wang@linux.alibaba.com>,
	tj@kernel.org, mkoutny@suse.com
Subject: Re: [PATCH mm-new v1 2/2] mm: vmscan: skip anon folios zswap would refuse when writeback is off
Date: Sat, 31 Oct 2026 09:53:12 +0000	[thread overview]
Message-ID: <sashiko-outbox-191879@kernel.org> (raw)
In-Reply-To: <20261030095223.325309-3-jiayuan.chen@linux.dev>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] mm: vmscan: unmapped swapcache folios bypass swap_writeout_blocked()
- [Medium] mm: vmscan: large and split head folios bypass swap_writeout_blocked()
--

commit 6d6f21b971f8e3a837e970c969d5f1fdbeb97cbe
Author: Jiayuan Chen <jiayuan.chen@linux.dev>

mm: vmscan: skip anon folios zswap would refuse when writeback is off

This commit adds swap_writeout_blocked() and zswap_store_blocked() to detect
when a memcg with memory.zswap.writeback=0 cannot store pages in zswap. In
shrink_folio_list(), non-zero-filled anonymous folios are activated early
before allocating swap slots and unmapping them when swap writeout is
blocked.

> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 7ddef3b488b3d..591a98cd9ef59 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -1169,6 +1169,7 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
>  	unsigned int nr_reclaimed = 0, nr_demoted = 0;
>  	unsigned int pgactivate = 0;
>  	bool do_demote_pass;
> +	int swap_blocked = -1;
>  	struct swap_io_ctx ctx = {};
>  
>  	folio_batch_init(&free_folios);
> @@ -1355,6 +1356,22 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
>  			continue;
>  		}
>  
> +		/*
> +		 * swap_writeout() would only activate this folio. Do that now,
> +		 * before it costs a swap slot and an unmap. Zero-filled folios
> +		 * are marked in the swap table without a store, an unmapped one
> +		 * may only need its stale slot dropped and a large one may still
> +		 * be split, let them go on.
> +		 */
> +		if (folio_test_anon(folio) && folio_test_swapbacked(folio) &&
> +		    folio_mapped(folio) && !folio_test_large(folio) &&
> +		    (!folio_test_swapcache(folio) || folio_test_dirty(folio))) {
> +			if (swap_blocked < 0)
> +				swap_blocked = swap_writeout_blocked(memcg);
> +			if (swap_blocked && !is_folio_zero_filled(folio))
> +				goto activate_locked;
> +		}

[Severity: Medium]
Does requiring folio_mapped(folio) allow unmapped dirty swap-cache folios
with active swap PTEs to bypass this check on subsequent reclaim passes?

When try_to_unmap() unmaps an anonymous folio during reclaim,
try_to_unmap_one() installs non-present swap PTEs via folio_dup_swap() and
removes the present PTEs. Once swap_writeout() rejects the folio with
AOP_WRITEPAGE_ACTIVATE (for example, when a clean swap-cache folio had a
dirty PTE settled by unmap, or when a folio was in the batch as zswap
became full), the folio stays in the swap cache marked dirty with
folio_mapped(folio) == false and folio_maybe_swapped(folio) == true.

If userspace does not fault the cold folio back in, folio_mapped(folio)
remains false on every subsequent pass through shrink_folio_list(), so the
folio skips this check and enters pageout() -> swap_writeout():

mm/page_io.c:swap_writeout() {
    if (folio_free_swap(folio))
        goto out_unlock;
    if (is_folio_zero_filled(folio)) {
        ...
    }
    swap_zeromap_folio_clear(folio);
    if (zswap_store(folio)) {
        ...
    }
    if (!mem_cgroup_zswap_writeback_enabled(folio_memcg(folio)))
        return AOP_WRITEPAGE_ACTIVATE;
}

Because folio_free_swap() checks folio_maybe_swapped(folio), it returns
false when swap PTEs still reference the slot:

mm/swapfile.c:folio_free_swap() {
    if (!folio_swapcache_freeable(folio))
        return false;
    if (folio_maybe_swapped(folio))
        return false;
    ...
}

Wouldn't those unmapped swap-cache folios repeatedly run
is_folio_zero_filled(), swap_zeromap_folio_clear(), and zswap_store() on
every reclaim cycle instead of being activated early?

[Severity: Medium]
How does !folio_test_large(folio) interact with CONFIG_THP_SWAP and with
the head folio left behind by split_folio_to_list()?

First, when CONFIG_THP_SWAP is enabled, a normal fully-mapped large
anonymous folio is not split before folio_alloc_swap():

mm/vmscan.c:shrink_folio_list() {
    if (folio_test_large(folio)) {
        if (folio_expected_ref_count(folio) !=
            folio_ref_count(folio) - 1)
            goto activate_locked;
        if (data_race(!list_empty(&folio->_deferred_list) &&
            folio_test_partially_mapped(folio)) &&
            split_folio_to_list(folio, folio_list))
            goto activate_locked;
    }
    ret = folio_alloc_swap(folio);
}

When folio_alloc_swap() succeeds for the large folio without returning
-E2BIG, the entire large folio is unmapped by try_to_unmap() and rejected
in swap_writeout() with AOP_WRITEPAGE_ACTIVATE, and remains a large folio
on subsequent passes.

Second, even when split_folio_to_list(folio, folio_list) splits a large
folio (either for a partially-mapped folio on _deferred_list or when
folio_alloc_swap() returns -E2BIG), only the tail pages are queued onto
folio_list to re-enter the loop. The head folio (now order-0) continues
directly to folio_alloc_swap(folio) and try_to_unmap() in the current
iteration without passing through the swap_writeout_blocked() check.

Should large folios and split head folios also be checked against
swap_writeout_blocked() before allocating swap and unmapping?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261030095223.325309-1-jiayuan.chen@linux.dev?part=2


      parent reply	other threads:[~2026-10-31  9:53 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30  9:52 [PATCH mm-new v1 0/2] mm: vmscan: skip anon folios zswap would refuse when writeback is off Jiayuan Chen
2026-10-30  9:52 ` [PATCH mm-new v1 1/2] mm: memcontrol: split mem_cgroup_may_zswap() out of obj_cgroup_may_zswap() Jiayuan Chen
2026-10-30  9:52 ` [PATCH mm-new v1 2/2] mm: vmscan: skip anon folios zswap would refuse when writeback is off Jiayuan Chen
2026-10-30 17:52   ` Yosry Ahmed
2026-10-30 19:03     ` Barry Song
2026-10-30 22:32       ` Yosry Ahmed
2026-10-31  9:53   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-191879@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=axelrasmussen@google.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=baoquan.he@linux.dev \
    --cc=chengming.zhou@linux.dev \
    --cc=chrisl@kernel.org \
    --cc=david@kernel.org \
    --cc=hannes@cmpxchg.org \
    --cc=jiayuan.chen@linux.dev \
    --cc=kasong@tencent.com \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mhocko@kernel.org \
    --cc=mkoutny@suse.com \
    --cc=muchun.song@linux.dev \
    --cc=nphamcs@gmail.com \
    --cc=qi.zheng@linux.dev \
    --cc=roman.gushchin@linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=shakeel.butt@linux.dev \
    --cc=shikemeng@huaweicloud.com \
    --cc=tj@kernel.org \
    --cc=weixugc@google.com \
    --cc=yosry@kernel.org \
    --cc=youngjun.park@lge.com \
    --cc=yuanchu@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.