From: Kairui Song <ryncsn@gmail.com>
To: mt4022@columbia.edu
Cc: Andrew Morton <akpm@linux-foundation.org>,
Chris Li <chrisl@kernel.org>,
Kemeng Shi <shikemeng@huaweicloud.com>,
Nhat Pham <nphamcs@gmail.com>, Baoquan He <baoquan.he@linux.dev>,
Barry Song <baohua@kernel.org>,
Youngjun Park <youngjun.park@lge.com>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
tz2294@columbia.edu, stable@vger.kernel.org
Subject: Re: [PATCH] mm, swap: stop cluster list scans once swapoff clears SWP_WRITEOK
Date: Thu, 29 Oct 2026 21:57:58 +0800 [thread overview]
Message-ID: <CAMgjq7AxShdVC6Ax+5USBSCb3c92MC3UVGLucxN47W_EmVcwDw@mail.gmail.com> (raw)
In-Reply-To: <20261026-swapoff-writeok-v1-1-4f4cd420aa33@columbia.edu>
On Mon, Oct 26, 2026 at 11:21 PM Mani Tofigh via B4 Relay
<devnull+mt4022.columbia.edu@kernel.org> wrote:
>
> From: Mani Tofigh <mt4022@columbia.edu>
>
> SWP_WRITEOK is cleared under si->lock when swapoff starts so that no
> new swap entries are allocated from the device, and try_to_unuse()
> then waits for writeback of the folios that still use the device.
>
> However, an order-0 allocation that chose the device can race with
> swapoff clearing the flag and then loop forever. It takes a cluster
> from the fragment cluster list of a higher order with
> isolate_lock_cluster(), the allocation from that cluster fails because
> SWP_WRITEOK is clear, and relocate_cluster() puts the cluster back on
> the same list, so the loop keeps finding clusters on it.
>
> The looping task is in reclaim, and it may have set writeback on a
> folio whose write has been queued but not yet submitted. swapoff then
> waits for that folio, while the reclaiming task, stuck in the loop,
> never reaches the point where it submits the write:
>
> CPU0 (reclaim) CPU1 (swapoff)
> -------------- --------------
> shrink_folio_list()
> pageout(F)
> /* F under writeback,
> write not submitted */
> folio_alloc_swap(G) /* order 0 */
> swap_alloc_slow()
> del_from_avail_list(si, true)
> si->flags &= ~SWP_WRITEOK
> try_to_unuse()
> folio_wait_writeback(F)
> cluster_alloc_swap_entry()
> isolate_lock_cluster()
> /* allocation fails:
> !SWP_WRITEOK */
> relocate_cluster()
> /* repeats forever */
> swap_write_submit()
> /* never reached */
>
> This results in a soft lockup on CPU0 and a swapoff that never returns.
>
> isolate_lock_cluster() used to check SWP_WRITEOK under si->lock until
> commit 07adc4cf1ecd ("mm, swap: implement dynamic allocation of swap
> table") removed that check. Restore the check. Since swapoff clears
> the flag under the same lock, isolate_lock_cluster() then returns NULL,
> which ends the scan; the swap slot allocation for G fails, and reclaim
> goes on to submit the write of F.
>
> Given that this is a race, its reproducibility is non-deterministic.
> We reproduce it by using a non-rotational swap device that is not
> synchronous, with 64K mTHP enabled, where one CPU repeatedly pages out
> two 64K folios and a 4K page with madvise(MADV_PAGEOUT), while other
> CPUs run swapoff and swapon on the same device. In my tests it
> triggers within 20s in each run, and with the fix applied, it runs for
> 300s without any hang/kernel report. This bug was found and
> reproduced deterministically using a deterministic hypervisor that is
> currently under development.
>
> Fixes: 07adc4cf1ecd ("mm, swap: implement dynamic allocation of swap table")
> Cc: stable@vger.kernel.org
> Signed-off-by: Mani Tofigh <mt4022@columbia.edu>
> ---
> Before commit dda8fb68b590 ("mm/swap: also use struct swap_iocb for
> block I/O"), reclaim submitted the swap write right away, so on 6.18
> to 7.2 the loop most likely ends once swapoff frees the slots instead
> of hanging.
>
> Reproducer: https://github.com/manitofigh/swapoff-repro/blob/main/swapoff.c
> ---
> mm/swapfile.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index 601979b97f95..2b03d14b5f52 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
> @@ -627,6 +627,10 @@ static struct swap_cluster_info *isolate_lock_cluster(
> u8 flags = CLUSTER_FLAG_NONE;
>
> spin_lock(&si->lock);
> +
> + if (unlikely(!(si->flags & SWP_WRITEOK)))
> + goto out;
Nice catch!
> list_for_each_entry(ci, list, list) {
> if (!spin_trylock(&ci->lock))
> continue;
> @@ -642,6 +646,7 @@ static struct swap_cluster_info *isolate_lock_cluster(
> found = ci;
> break;
> }
> +out:
> spin_unlock(&si->lock);
>
> /* Cluster's table is freed when and only when it's on the free list. */
Acked-by: Kairui Song <kasong@tencent.com>
prev parent reply other threads:[~2026-10-29 13:58 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 15:19 Mani Tofigh via B4 Relay
2026-10-29 13:57 ` Kairui Song [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=CAMgjq7AxShdVC6Ax+5USBSCb3c92MC3UVGLucxN47W_EmVcwDw@mail.gmail.com \
--to=ryncsn@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baoquan.he@linux.dev \
--cc=chrisl@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mt4022@columbia.edu \
--cc=nphamcs@gmail.com \
--cc=shikemeng@huaweicloud.com \
--cc=stable@vger.kernel.org \
--cc=tz2294@columbia.edu \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®