Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Nhat Pham" <nphamcs@gmail.com>, akpm@linux-foundation.org
Cc: chrisl@kernel.org, kasong@tencent.com, hannes@cmpxchg.org,
	mhocko@kernel.org, roman.gushchin@linux.dev,
	shakeel.butt@linux.dev, yosry@kernel.org, david@kernel.org,
	muchun.song@linux.dev, shikemeng@huaweicloud.com,
	baoquan.he@linux.dev, baohua@kernel.org, youngjun.park@lge.com,
	chengming.zhou@linux.dev, ljs@kernel.org, liam@infradead.org,
	vbabka@kernel.org, rppt@kernel.org, surenb@google.com,
	qi.zheng@linux.dev, axelrasmussen@google.com, yuanchu@google.com,
	weixugc@google.com, riel@surriel.com, gourry@gourry.net,
	haowenchao22@gmail.com, corbet@lwn.net, hughd@google.com,
	baolin.wang@linux.alibaba.com, tj@kernel.org, mkoutny@suse.com,
	skhan@linuxfoundation.org, rdunlap@infradead.org,
	kunwu.chan@linux.dev, kernel-team@meta.com, linux-mm@kvack.org
Subject: Re: [PATCH v6 07/12] mm, swap: reclaim physical slots backing cache-only vswap entries
Date: Thu, 22 Oct 2026 06:19:20 +0000	[thread overview]
Message-ID: <sashiko-outbox-181081@kernel.org> (raw)
In-Reply-To: <20261021060323.553433-8-nphamcs@gmail.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] mm, swap: try_to_reclaim_vswap_backing() overshoots physical run when i > 0
- [Medium] mm, swap: folio_realloc_swap() misses SWP_RMAP_CACHE_ONLY when swap_count is 0
- [Medium] mm, swap: missing vswap cache-only reclaim in cluster_scan_range()
--

commit e03ce92e52f674a68e33585bcd17c229fae53bf0
Author: Nhat Pham <nphamcs@gmail.com>

mm, swap: reclaim physical slots backing cache-only vswap entries

This commit introduces a SWP_RMAP_CACHE_ONLY flag on physical swap table rmap
entries when a vswap slot's swap count drops to zero while its folio remains
in the swap cache. It teaches swap_reclaim_full_clusters() to reclaim those
physical backing slots via try_to_reclaim_vswap_backing().

> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index be66ac79313ce..640e38a55497e 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
[ ... ]
> @@ -877,6 +882,55 @@ static int swap_cluster_setup_bad_slot(struct swap_info_struct *si,
>  	return ret;
>  }
>  
> +/*
> + * Try to reclaim a Vswap-tagged physical slot backing a vswap entry.
> + * The physical cluster lock must NOT be held. Returns the backing folio's
> + * page count, negated if the slots could not be reclaimed, or 0 if the
> + * folio could not be shown to own @offset (i.e. there is a race).
> + */
> +static int try_to_reclaim_vswap_backing(struct swap_info_struct *si,
> +					unsigned long offset,
> +					swp_entry_t vswap_entry)
> +{
> +	swp_entry_t phys_base;
> +	struct folio *folio;
> +	unsigned int i;
> +	int ret;
> +
> +	folio = swap_cache_get_folio(vswap_entry);
> +	if (!folio)
> +		return 0;
> +
> +	if (!folio_trylock(folio)) {
> +		/* Unvalidated: the folio may not even own @offset. */
> +		folio_put(folio);
> +		return 0;
> +	}
> +
> +	if (!folio_matches_swap_entry(folio, vswap_entry)) {
> +		folio_unlock(folio);
> +		folio_put(folio);
> +		return 0;
> +	}
> +
> +	i = vswap_entry.val - folio->swap.val;
> +	phys_base = vswap_to_phys(folio->swap);
> +	if (!phys_base.val || swp_type(phys_base) != si->type ||
> +	    swp_offset(phys_base) + i != offset) {
> +		folio_unlock(folio);
> +		folio_put(folio);
> +		return 0;
> +	}
> +
> +	/* The run is ours: skip it all, whether or not the free succeeds. */
> +	ret = folio_nr_pages(folio);
> +	if (!folio_free_swap(folio))
> +		ret = -ret;
> +	folio_unlock(folio);
> +	folio_put(folio);
> +	return ret;
> +}

[Severity: Medium]
When try_to_reclaim_vswap_backing() in mm/swapfile.c is called for a large
folio at a non-zero subpage offset (where i = vswap_entry.val -
folio->swap.val > 0 and offset == swp_offset(phys_base) + i), doesn't
returning folio_nr_pages(folio) cause swap_reclaim_full_clusters() to
overshoot the end of the folio's physical slot run by i slots?

This can happen when scanning a cluster in swap_reclaim_full_clusters() if:
- subpages 0..i-1 still have swap_count > 0 (so SWP_RMAP_CACHE_ONLY is not
  set on them) while subpage i had its swap_count drop to 0 via single-PTE
  swap-in or partial unmap,
- folio_trylock(folio) fails on subpages 0..i-1 and then succeeds on
  subpage i, or
- a concurrent swap_put_entries_cluster() marks the folio's subpages
  SWP_RMAP_CACHE_ONLY after the scanner has already advanced past subpages
  0..i-1.

Because swap_reclaim_full_clusters() advances offset by:

    offset += abs(nr_reclaim);

returning folio_nr_pages(folio) instead of folio_nr_pages(folio) - i advances
offset to swp_offset(phys_base) + i + folio_nr_pages(folio), skipping the
first i physical slots of the following folio(s) in the cluster. Should ret
be initialized to folio_nr_pages(folio) - i instead?

> @@ -1182,6 +1237,19 @@ static void swap_reclaim_full_clusters(struct swap_info_struct *si, bool force)
>  					offset += abs(nr_reclaim);
>  					continue;
>  				}
> +			} else if (swp_tb_is_vswap(swp_tb) &&
> +				   (swp_tb & SWP_RMAP_CACHE_ONLY)) {
> +				vswap_entry = swp_tb_vswap_to_swp_entry(swp_tb);
> +				spin_unlock(&ci->lock);
> +				nr_reclaim = try_to_reclaim_vswap_backing(si, offset,
> +									  vswap_entry);
> +				ci = swap_cluster_lock(si, offset);
> +				if (!ci)
> +					goto next;
> +				if (nr_reclaim) {
> +					offset += abs(nr_reclaim);
> +					continue;
> +				}
>  			}
>  			offset++;
>  		}

[Severity: Medium]
While swap_reclaim_full_clusters() in mm/swapfile.c is updated here to
reclaim SWP_RMAP_CACHE_ONLY entries from full clusters, what about non-full
or fragmented clusters scanned during allocation via
folio_alloc_phys_swap() -> alloc_swap_scan_cluster()?

In mm/swapfile.c, cluster_scan_range() currently only checks for direct
folio entries with swap_count == 0:

    if (swp_tb_is_folio(swp_tb) && !__swp_tb_get_count(swp_tb)) {
        if (swap_is_vswap(si) || !vm_swap_full())
            return false;
        *need_reclaim = true;
        continue;
    }
    /* Slot with zero count can only be NULL or folio */
    VM_WARN_ON(!swp_tb_get_count(swp_tb));
    return false;

And cluster_reclaim_range() stops on any entry where swp_tb_get_count(swp_tb)
is non-zero (which returns -EINVAL for Vswap-tagged entries):

    do {
        swp_tb = swap_table_get(ci, offset % SWAPFILE_CLUSTER);
        if (swp_tb_get_count(swp_tb))
            break;
        if (swp_tb_is_folio(swp_tb))
            if (__try_to_reclaim_swap(si, offset, TTRS_ANYWAY) < 0)
                break;
    } while (++offset < end);

If a physical cluster has some free slots (ci->count < SWAPFILE_CLUSTER, so
it sits on percpu_swap_cluster, global_cluster, si->nonfull_clusters[order],
or si->frag_clusters[order] rather than si->full_clusters), won't
SWP_RMAP_CACHE_ONLY slots in that cluster prevent cluster_scan_range() and
cluster_reclaim_range() from reclaiming them when vm_swap_full() is true,
causing high-order physical swap allocations to fail prematurely?

[ ... ]
> @@ -1752,6 +1820,8 @@ static void swap_put_entries_cluster(struct swap_info_struct *si,
>  			}
>  			/* count will be 0 after put, slot can be reclaimed */
>  			need_reclaim = true;
> +			if (swap_is_vswap(si))
> +				vswap_mark_cache_only(ci, ci_off);
>  		}
>  		/*
>  		 * A count != 1 or cached slot can't be freed. Put its swap
[ ... ]
> @@ -2008,6 +2080,51 @@ int folio_alloc_swap(struct folio *folio)
>  	return order ? -E2BIG : -ENOMEM;
>  }
>  
> +static void vswap_mark_cache_only(struct swap_cluster_info *ci,
> +				  unsigned int ci_off)
> +{
> +	struct swap_cluster_info_dynamic *ci_dyn;
> +	struct swap_cluster_info *pci;
> +	swp_entry_t phys;
> +	unsigned long vt;
> +
> +	ci_dyn = container_of(ci, struct swap_cluster_info_dynamic, ci);
> +	vt = __vtable_get(ci_dyn, ci_off);
> +
> +	if (vtable_type(vt) == VSWAP_SWAPFILE) {
> +		phys = vtable_to_phys(vt);
> +		pci = __swap_entry_to_cluster(phys);
> +		swap_rmap_mark_cache_only(pci, swp_cluster_offset(phys));
> +	}
> +}

[Severity: Medium]
Can physical swap slots remain unmarked with SWP_RMAP_CACHE_ONLY if a vswap
slot already has swap_count == 0 when folio_realloc_swap() in mm/swapfile.c
installs its physical backing?

In folio_realloc_swap(), folio_alloc_phys_swap() ->
__swap_cluster_alloc_entries() initializes the physical swap_table entries
with swp_entry_to_swp_tb_vswap(v) (with SWP_RMAP_CACHE_ONLY clear), and then
folio_realloc_swap() writes VSWAP_SWAPFILE into virtual_table under ci->lock:

    for (i = 0; i < nr; i++) {
        pe.val = phys_entry.val + i;
        __vtable_set(ci_dyn, voff + i, vtable_mk_phys(pe));
    }

However, vswap_mark_cache_only() only sets SWP_RMAP_CACHE_ONLY during
swap_put_entries_cluster() when swap_count transitions from 1 to 0 and
vtable_type(vt) is already VSWAP_SWAPFILE. A vswap slot can already have
swap_count == 0 when folio_realloc_swap() installs VSWAP_SWAPFILE in two
cases:

1. During swap_writeout() or zswap_writeback_entry(), the folio is locked
   while virtual_table is still VSWAP_NONE or VSWAP_ZSWAP. A concurrent PTE
   zap (such as munmap() or MADV_DONTNEED calling free_swap_and_cache_nr() ->
   swap_put_entries_direct() -> swap_put_entries_cluster()) can drop the
   vswap slot's swap_count from 1 to 0 before folio_realloc_swap() sets
   VSWAP_SWAPFILE. At that point vswap_mark_cache_only() sees
   vtable_type(vt) != VSWAP_SWAPFILE, and __try_to_reclaim_swap() cannot
   lock the folio.
2. When swapping out a partially unmapped large folio (mTHP), some subpages
   may already have swap_count == 0 while other subpages have swap_count > 0.

Since swap_count is already 0, swap_put_entries_cluster() will not run on
those slots again. Should folio_realloc_swap() check whether
__swp_tb_get_count(__swap_table_get(ci, voff + i)) == 0 while holding
ci->lock and mark the corresponding physical rmap entry with
SWP_RMAP_CACHE_ONLY?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261021060323.553433-1-nphamcs@gmail.com?part=7


  reply	other threads:[~2026-10-22  6:19 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21  6:03 [PATCH v6 00/12] Virtual Swap Space (Swap Table Edition) Nhat Pham
2026-10-21  6:03 ` [PATCH v6 01/12] mm, swap: add virtual swap device infrastructure Nhat Pham
2026-10-21  6:03 ` [PATCH v6 02/12] mm, swap: support zswap and zero-filled swap pages as vswap backends Nhat Pham
2026-10-21  6:03 ` [PATCH v6 03/12] mm, swap: prepare the swap IO path for vswap Nhat Pham
2026-10-21  6:03 ` [PATCH v6 04/12] mm, swap: support physical swap as a vswap backend Nhat Pham
2026-10-22  6:19   ` sashiko-bot
2026-10-21  6:03 ` [PATCH v6 05/12] mm, swap: enable THP swapin for vswap entries Nhat Pham
2026-10-22  6:19   ` sashiko-bot
2026-10-21  6:03 ` [PATCH v6 06/12] mm, swap: write back vswap zswap entries to physical swap Nhat Pham
2026-10-22  6:19   ` sashiko-bot
2026-10-21  6:03 ` [PATCH v6 07/12] mm, swap: reclaim physical slots backing cache-only vswap entries Nhat Pham
2026-10-22  6:19   ` sashiko-bot [this message]
2026-10-21  6:03 ` [PATCH v6 08/12] mm, swap: only charge physical swap entries Nhat Pham
2026-10-22  6:19   ` sashiko-bot
2026-10-21  6:03 ` [PATCH v6 09/12] mm, swap: add debugfs counters for vswap Nhat Pham
2026-10-21  6:18 ` [PATCH v6 10/12] mm, swap: defer memcg_table allocation for physical swap clusters Nhat Pham
2026-10-22  6:19   ` sashiko-bot
2026-10-21  6:18 ` [PATCH v6 11/12] mm, swap: add vmstat and memcg counters for vswap Nhat Pham
2026-10-21  6:18 ` [RFC PATCH v6 12/12] mm, swap: back vswap clusters with a VM_SPARSE array Nhat Pham
2026-10-22  6:19   ` sashiko-bot
2026-10-22  2:36 ` [PATCH v6 00/12] Virtual Swap Space (Swap Table Edition) Gregory Price (Meta)
2026-10-22 17:03   ` Nhat Pham

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-181081@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=corbet@lwn.net \
    --cc=david@kernel.org \
    --cc=gourry@gourry.net \
    --cc=hannes@cmpxchg.org \
    --cc=haowenchao22@gmail.com \
    --cc=hughd@google.com \
    --cc=kasong@tencent.com \
    --cc=kernel-team@meta.com \
    --cc=kunwu.chan@linux.dev \
    --cc=liam@infradead.org \
    --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=rdunlap@infradead.org \
    --cc=riel@surriel.com \
    --cc=roman.gushchin@linux.dev \
    --cc=rppt@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=shakeel.butt@linux.dev \
    --cc=shikemeng@huaweicloud.com \
    --cc=skhan@linuxfoundation.org \
    --cc=surenb@google.com \
    --cc=tj@kernel.org \
    --cc=vbabka@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox