Re: [PATCH v3 06/14] mm, swap: add xswap grow trigger on cluster allocation

From: Chris Li

Date: Sun Sep 27 2026 - 21:55:19 EST


On Wed, Sep 16, 2026 at 12:20 AM Baoquan He <hebaoquan@xxxxxxxxxx> wrote:
>
> When cluster_alloc_swap_entry() fails to find a free cluster and
> the xswap device still has room to grow, expand the mapped range
> by XSWAP_GROW_CLUSTERS clusters.
>
> Since xswap is always SWP_SOLIDSTATE, global_cluster_lock is never
> held on this path.
>
> This makes the xswap cluster space grow transparently as swap usage
> increases, without any userspace intervention.
>
> Growing maps pages into the VM_SPARSE area, which can sleep. The caller
> holds local_lock(&percpu_swap_cluster.lock) across the whole slow path,
> so drop it around xswap_map_clusters() and take it again afterwards; it
> only protects the per-cpu cluster cache, which this path does not touch.
>
> Signed-off-by: Baoquan He <hebaoquan@xxxxxxxxxx>
> ---
> mm/swapfile.c | 41 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 41 insertions(+)
>
> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index b0edf5421fc5..17e482059d32 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
> @@ -1273,6 +1273,47 @@ static unsigned long cluster_alloc_swap_entry(struct swap_info_struct *si,
> if (found)
> goto done;
> }
> +
> +#ifdef CONFIG_XSWAP
> + /* For xswap: grow the cluster_info array, then retry. */
> + if (!found && (si->flags & SWP_XSWAP) &&

I see oppertunity for more swap_ops

Chris

> + READ_ONCE(si->nr_clusters_mapped) < READ_ONCE(si->nr_clusters_max) &&
> + list_empty(&si->free_clusters)) {
> + unsigned long nr_new = min(READ_ONCE(si->nr_clusters_max) -
> + READ_ONCE(si->nr_clusters_mapped),
> + XSWAP_GROW_CLUSTERS);
> + unsigned long start = READ_ONCE(si->nr_clusters_mapped);
> + unsigned long i;
> + int ret;
> +
> + /*
> + * Mapping pages can sleep. The lock only guards the per-cpu
> + * cluster cache, which this path does not touch.
> + */
> + local_unlock(&percpu_swap_cluster.lock);
> + ret = xswap_map_clusters(si, start, nr_new);
> + local_lock(&percpu_swap_cluster.lock);
> +
> + if (!ret) {
> + for (i = start; i < start + nr_new; i++) {
> + struct swap_cluster_info *ci = &si->cluster_info[i];
> +
> + /*
> + * A concurrent grower may have taken these already;
> + * only add the off-list ones.
> + */
> + spin_lock(&ci->lock);
> + if (ci->flags == CLUSTER_FLAG_NONE)
> + move_cluster(si, ci, &si->free_clusters,
> + CLUSTER_FLAG_FREE);
> + spin_unlock(&ci->lock);
> + }
> +
> + found = alloc_swap_scan_list(si, &si->free_clusters,
> + folio, false);
> + }
> + }
> +#endif
> done:
> if (!(si->flags & SWP_SOLIDSTATE))
> spin_unlock(&si->global_cluster_lock);
> --
> 2.54.0
>