All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Lian Wang" <lianux.mm@gmail.com>, linux-mm@kvack.org
Cc: akpm@linux-foundation.org, chrisl@kernel.org, nphamcs@gmail.com,
	baoquan.he@linux.dev, baohua@kernel.org, youngjun.park@lge.com,
	shikemeng@huaweicloud.com, kasong@tencent.com,
	linjh22s@gmail.com, kunwu.chan@linux.dev, kees@kernel.org,
	gustavoars@kernel.org, tglx@kernel.org
Subject: Re: [RFC PATCH v3 12/18] mm/swap: bound synchronous discard during allocation
Date: Wed, 28 Oct 2026 16:14:58 +0000	[thread overview]
Message-ID: <sashiko-outbox-188141@kernel.org> (raw)
In-Reply-To: <1de96c68db9d3ac83ae485a1eddabcea1a2524a1.1793106426.git.lianux.mm@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mm/swap: synchronous discard under swapon_rwsem in cluster_alloc_swap_entry()
- [Medium] mm: premature swap allocation failure in cluster_alloc_swap_entry()
--

commit 148b9ddc65b48866a2eb76b6131e127b4a148727
Author: Kairui Song <kasong@tencent.com>

mm/swap: bound synchronous discard during allocation

This commit replaces the global synchronous discard fallback with a
per-device single-cluster discard in cluster_alloc_swap_entry() when a
swap device's free cluster list is empty.

Link: https://lore.kernel.org/all/CAMgjq7CsYhEjvtN85XGkrONYAJxve7gG593TFeOGV-oax++kWA@mail.gmail.com/

> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index 0a16343a5b03c..5c26682c0bab2 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c

[ ... ]

> @@ -1576,6 +1585,15 @@ static unsigned long cluster_alloc_swap_entry(struct swap_info_struct *si,
>  		found = alloc_swap_scan_list(si, &si->free_clusters, folio, false);
>  		if (found)
>  			goto done;
> +
> +		if (!discarded) {
> +			swap_alloc_unlock_device(si);
> +			if (swap_discard_one_cluster(si)) {
> +				discarded = true;
> +				goto restart;
> +			}
> +			swap_alloc_lock_device(si);
> +		}
>  	}

[Severity: High]
Is it safe to call swap_discard_one_cluster() here while holding
swapon_rwsem for reading?

Previously, swap_sync_discard() dropped swapon_rwsem after pinning the
swap device before issuing synchronous discard I/O:

mm/swapfile.c:swap_sync_discard() {
	...
	plist_for_each_entry_safe(si, next, &swap_active_head, list) {
		percpu_up_read(&swapon_rwsem);
		if (get_swap_device_info(si)) {
			if (si->flags & SWP_PAGE_DISCARD)
				ret = swap_do_scheduled_discard(si);
			put_swap_device(si);
		}
	...
}

With this change, swap_alloc_entry() holds percpu_down_read(&swapon_rwsem)
and migrate_disable() across cluster_alloc_swap_entry(), which only drops
the device cluster lock via swap_alloc_unlock_device(si) before calling
swap_discard_one_cluster() -> discard_swap_cluster() ->
blkdev_issue_discard(..., GFP_NOIO) -> submit_bio_wait().

If a writer such as swapon(), swapoff(), or pin_hibernation_swap_type()
calls percpu_down_write(&swapon_rwsem) while submit_bio_wait() is waiting
for the REQ_OP_DISCARD bio to complete, new readers calling
percpu_down_read(&swapon_rwsem) in swap_alloc_entry() will block behind
the writer. Could this stall system-wide swap allocation, or deadlock if
a block backend worker (such as ublk or nbd) needs memory reclaim and swap
allocation to complete the in-flight discard request?

Also, on loop-backed swap devices, processing REQ_OP_DISCARD in the loop
worker calls lo_fallocate() -> file->f_op->fallocate(), which acquires
inode_lock() on the backing file. Since swapon() holds inode_lock() while
calling swap_queue_lock_for_add() -> percpu_down_write(&swapon_rwsem),
doesn't waiting for discard completion under percpu_down_read(&swapon_rwsem)
invert the lock ordering between inode_lock() and swapon_rwsem?

[ ... ]

> @@ -2149,14 +2134,8 @@ int folio_alloc_swap(struct folio *folio)
>  		}
>  	}
>  
> -again:
>  	swap_alloc_entry(folio);
>  
> -	if (!order && unlikely(!folio_test_swapcache(folio))) {
> -		if (swap_sync_discard())
> -			goto again;
> -	}

[Severity: Medium]
Can removing the post-reclaim discard retry cause folio_alloc_swap() to
fail prematurely when si->discard_clusters is populated during cluster
reclaim?

In mm/swapfile.c, when cluster_alloc_swap_entry() is entered on a
SWP_PAGE_DISCARD device with both si->free_clusters and
si->discard_clusters initially empty, alloc_swap_scan_list() on
si->free_clusters and swap_discard_one_cluster() both return false,
leaving discarded set to false.

cluster_alloc_swap_entry() then scans si->nonfull_clusters, calls
swap_reclaim_full_clusters(), and scans si->frag_clusters. During these
steps, cluster_reclaim_range() and swap_reclaim_full_clusters() call
__try_to_reclaim_swap() -> swap_cache_del_folio() ->
__swap_cluster_free_entries(). When reclaiming unmapped swapcache folios
drops a cluster's count to zero, free_cluster() schedules it for discard:

mm/swapfile.c:free_cluster() {
	if ((si->flags & (SWP_WRITEOK | SWP_PAGE_DISCARD)) ==
	    (SWP_WRITEOK | SWP_PAGE_DISCARD)) {
		swap_cluster_schedule_discard(si, ci);
		return;
	}
	...
}

Because swap_cluster_schedule_discard() sets ci->flags to
CLUSTER_FLAG_DISCARD and moves the cluster to si->discard_clusters,
cluster_reclaim_range() sees !cluster_is_usable(ci, order) and cannot
allocate from the cluster it just emptied:

mm/swapfile.c:cluster_reclaim_range() {
	...
	spin_lock(&ci->lock);
	/* Recheck after lock drop, another user might have used it. */
	if (!cluster_is_usable(ci, order)) {
		*usable = false;
		return false;
	}
	...
}

Previously, folio_alloc_swap() called swap_sync_discard() after
swap_alloc_entry() failed and retried the allocation, which discarded the
clusters newly queued to si->discard_clusters during reclaim. Now,
cluster_alloc_swap_entry() never checks si->discard_clusters again after
the nonfull, full, and fragment cluster scans, returning
SWAP_ENTRY_INVALID even though discarded is still false and reclaim just
queued discardable clusters.

In addition, when swap_discard_one_cluster() does discard a cluster, it
places the cluster onto the shared si->free_clusters list via
__free_cluster() and releases ci->lock while the caller does not hold
swap_alloc_lock_device(si). If another CPU isolates that cluster from
si->free_clusters before the discarding task re-acquires
swap_alloc_lock_device(si) at the restart label, won't the discarding task
see an empty si->free_clusters list and fail the allocation even when more
clusters remain on si->discard_clusters?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793106426.git.lianux.mm@gmail.com?part=12


  reply	other threads:[~2026-10-28 16:15 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 15:58 [RFC PATCH v3 00/18] mm/swap: introduce priority queues for swap device allocation Lian Wang
2026-10-27 16:03 ` [RFC PATCH v3 01/18] mm/swap: remove unused parameter for reading swap header Lian Wang
2026-10-27 16:03 ` [RFC PATCH v3 02/18] mm/swap: slightly cleanup the code for hibernation error handling Lian Wang
2026-10-27 16:04 ` [RFC PATCH v3 03/18] mm/swap: cleanup and document swap device availability flag usage Lian Wang
2026-10-28 16:14   ` sashiko-bot
2026-10-27 16:05 ` [RFC PATCH v3 04/18] mm/swap: introduce swap device iteration helper Lian Wang
2026-10-27 16:05 ` [RFC PATCH v3 05/18] mm/swap: change the swapon lock into a percpu rwsem Lian Wang
2026-10-28 16:14   ` sashiko-bot
2026-10-27 16:06 ` [RFC PATCH v3 06/18] mm/swap: serialize swap publication before removing swapon mutex Lian Wang
2026-10-27 16:07 ` [RFC PATCH v3 07/18] mm/swap: consolidate swap inuse accounting helpers Lian Wang
2026-10-27 16:07 ` [RFC PATCH v3 08/18] mm/swap: change back to use each swap device's percpu cluster Lian Wang
2026-10-27 16:08 ` [RFC PATCH v3 09/18] mm/swap: avoid direct reclaim when populating swap cluster metadata Lian Wang
2026-10-27 16:08 ` [RFC PATCH v3 10/18] mm/swap: add priority queue for swap device allocation Lian Wang
2026-10-28 16:14   ` sashiko-bot
2026-10-27 16:09 ` [RFC PATCH v3 11/18] mm/swap: remove available list Lian Wang
2026-10-27 16:10 ` [RFC PATCH v3 12/18] mm/swap: bound synchronous discard during allocation Lian Wang
2026-10-28 16:14   ` sashiko-bot [this message]
2026-10-27 16:11 ` [RFC PATCH v3 13/18] mm/swap: drop swap active plist Lian Wang
2026-10-27 16:11 ` [RFC PATCH v3 14/18] lib/plist.c: remove requeue function Lian Wang
2026-10-27 16:12 ` [RFC PATCH v3 15/18] mm/swap: reuse per-CPU queue readers across priority changes Lian Wang
2026-10-27 16:12 ` [RFC PATCH v3 16/18] mm/swap: advance the global queue cursor directly between priority rings Lian Wang
2026-10-27 16:13 ` [RFC PATCH v3 17/18] mm/swap: skip reader rotation for single-device " Lian Wang
2026-10-27 16:14 ` [RFC PATCH v3 18/18] mm/swap: redistribute queue readers after membership changes Lian Wang

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-188141@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=baohua@kernel.org \
    --cc=baoquan.he@linux.dev \
    --cc=chrisl@kernel.org \
    --cc=gustavoars@kernel.org \
    --cc=kasong@tencent.com \
    --cc=kees@kernel.org \
    --cc=kunwu.chan@linux.dev \
    --cc=lianux.mm@gmail.com \
    --cc=linjh22s@gmail.com \
    --cc=linux-mm@kvack.org \
    --cc=nphamcs@gmail.com \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=shikemeng@huaweicloud.com \
    --cc=tglx@kernel.org \
    --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.