From: Baoquan He <baoquan.he@linux.dev>
To: Youngjun Park <youngjun.park@lge.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
Chris Li <chrisl@kernel.org>, Kairui Song <kasong@tencent.com>,
Kemeng Shi <shikemeng@huaweicloud.com>,
Nhat Pham <nphamcs@gmail.com>, Barry Song <baohua@kernel.org>,
her0gyugyu@gmail.com, linux-mm@kvack.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 2/2] mm/swap: scan by cluster in find_next_to_unuse()
Date: Fri, 7 Aug 2026 17:52:20 +0800 [thread overview]
Message-ID: <anWq1NW8xsmR3O80@MiWiFi-R3L-srv> (raw)
In-Reply-To: <20260806193228.458685-3-youngjun.park@lge.com>
On 08/07/26 at 04:32am, Youngjun Park wrote:
> find_next_to_unuse() walks every offset from 0 to si->max, and swapoff
> restarts that walk on each retry, so the cost scales with the size of
> the device rather than with the few slots the shmem and mmlist passes
> could not free. It has caused stalls before.
>
> The flat walk predates the swap table. Slot state now lives in a per
> cluster table, and wait_for_allocation() stops all allocation before
> try_to_unuse() runs, so a cluster that holds no slot in use stays that
> way. Skip such a cluster instead of reading all of its entries.
>
> Commit dc644a073769 ("mm: add three more cond_resched() in swapoff")
> answered those stalls with a cond_resched() every 256 offsets. A walk
> bounded by one cluster no longer needs that counter. The loop now runs
> at most SWAPFILE_CLUSTER times before it returns or reschedules, the
> same bound swap_reclaim_full_clusters() already scans between
> cond_resched() calls.
>
> The scan end is clamped to si->max, so the walk stops there rather than
> running into the masked tail of the last cluster.
>
> ci->count is read without ci->lock, so READ_ONCE() marks the read for
> KCSAN. Allocation is already stopped, so the count can only drop, and a
> slot stops being counted only after its folio has left the swap cache.
> An empty cluster therefore holds nothing for try_to_unuse() to act on.
>
> Signed-off-by: Youngjun Park <youngjun.park@lge.com>
> Reviewed-by: Barry Song <baohua@kernel.org>
> ---
> mm/swapfile.c | 43 ++++++++++++++++++++++++++++++-------------
> 1 file changed, 30 insertions(+), 13 deletions(-)
>
> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index dea2d3b36e06..0d24efd32eb0 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
> @@ -370,8 +370,6 @@ static void discard_swap_cluster(struct swap_info_struct *si,
> }
> }
>
> -#define LATENCY_LIMIT 256
> -
> static inline bool cluster_is_empty(struct swap_cluster_info *info)
> {
> return info->count == 0;
> @@ -2763,7 +2761,9 @@ static int unuse_mm(struct mm_struct *mm, unsigned int type)
> static unsigned int find_next_to_unuse(struct swap_info_struct *si,
> unsigned int prev)
> {
> - unsigned int i;
> + struct swap_cluster_info *ci;
> + unsigned long i, end;
> + unsigned int ci_off;
> unsigned long swp_tb;
>
> /*
> @@ -2772,19 +2772,36 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si,
> * hits are okay, and sys_swapoff() has already prevented new
> * allocations from this area (while holding swap_lock).
> */
> - for (i = prev + 1; i < si->max; i++) {
> - swp_tb = swap_table_get(__swap_offset_to_cluster(si, i),
> - i % SWAPFILE_CLUSTER);
> - if (!swp_tb_is_null(swp_tb) && !swp_tb_is_bad(swp_tb))
> - break;
> - if ((i % LATENCY_LIMIT) == 0)
> + i = prev + 1;
> + while (i < si->max) {
> + ci = __swap_offset_to_cluster(si, i);
> + end = min_t(unsigned long,
> + ALIGN_DOWN(i, SWAPFILE_CLUSTER) + SWAPFILE_CLUSTER,
> + si->max);
> +
> + /*
> + * An empty cluster has no slot in use, so skip it whole.
> + * A slot is uncounted only after its folio left the swap
> + * cache, so there is nothing here for try_to_unuse() to act on.
> + * Count only drops here, so a READ_ONCE() without ci->lock is
> + * enough, unlike in every other cluster_is_empty() caller.
> + */
> + if (!READ_ONCE(ci->count)) {
> + i = end;
> cond_resched();
> - }
> + continue;
> + }
>
> - if (i == si->max)
> - i = 0;
> + ci_off = i % SWAPFILE_CLUSTER;
> + for (; i < end; ci_off++, i++) {
> + swp_tb = swap_table_get(ci, ci_off);
> + if (!swp_tb_is_null(swp_tb) && !swp_tb_is_bad(swp_tb))
> + return i;
I would remove ci_off to save one local variable, but it's only personal
preference, not strong opinion.
for (; i < end; i++) {
swp_tb = swap_table_get(ci, i % SWAPFILE_CLUSTER);
...
}
Other than the nitpick, this is a great optimization patch.
Reviewed-by: Baoquan He <baoquan.he@linux.dev>
> + }
> + cond_resched();
> + }
>
> - return i;
> + return 0;
> }
>
> static int try_to_unuse(unsigned int type)
> --
> 2.48.1
>
>
next prev parent reply other threads:[~2026-08-07 9:52 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-06 19:32 [PATCH v3 0/2] mm/swap: skip empty clusters in the swapoff scan Youngjun Park
2026-08-06 19:32 ` [PATCH v3 1/2] mm/swap: fix stale comment on swap_info_struct::cluster_info Youngjun Park
2026-08-07 9:49 ` Baoquan He
2026-08-06 19:32 ` [PATCH v3 2/2] mm/swap: scan by cluster in find_next_to_unuse() Youngjun Park
2026-08-07 7:33 ` Kairui Song
2026-08-07 9:52 ` Baoquan He [this message]
2026-08-06 20:06 ` [PATCH v3 0/2] mm/swap: skip empty clusters in the swapoff scan Andrew Morton
2026-08-07 6:41 ` Youngjun Park
2026-08-07 6:59 ` Youngjun Park
2026-08-07 8:42 ` Baoquan He
2026-08-07 9:59 ` Youngjun Park
2026-08-07 21:42 ` Andrew Morton
2026-08-08 15:53 ` Youngjun Park
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=anWq1NW8xsmR3O80@MiWiFi-R3L-srv \
--to=baoquan.he@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=chrisl@kernel.org \
--cc=her0gyugyu@gmail.com \
--cc=kasong@tencent.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=nphamcs@gmail.com \
--cc=shikemeng@huaweicloud.com \
--cc=youngjun.park@lge.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.