All of lore.kernel.org
 help / color / mirror / Atom feed
From: Baoquan He <baoquan.he@linux.dev>
To: linux-mm@kvack.org
Cc: akpm@linux-foundation.org, chrisl@kernel.org, kasong@tencent.com,
	shikemeng@huaweicloud.com, nphamcs@gmail.com, baohua@kernel.org,
	youngjun.park@lge.com, hannes@cmpxchg.org, yosry@kernel.org,
	chengming.zhou@linux.dev, linux-kernel@vger.kernel.org,
	Baoquan He <baoquan.he@linux.dev>
Subject: [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth
Date: Tue,  7 Jul 2026 16:26:11 +0800	[thread overview]
Message-ID: <20260707082614.95030-10-baoquan.he@linux.dev> (raw)
In-Reply-To: <20260707082614.95030-1-baoquan.he@linux.dev>

Allocate additional physical pages and map them at the end of the
already-reserved sparse vmalloc area via vm_area_map_pages(). The flat
cluster_info pointer never moves — new clusters appear contiguously at
higher indices. New clusters are fully initialized (spinlocks, list
heads, trailing BAD mask) before si->max is published via WRITE_ONCE.

Page-pointer tracking in cluster_vm->pages is reallocated to accommodate
additional entries. Returns -E2BIG if the requested size exceeds the
reserved VA range. Extension is serialized by swapon_mutex.

Signed-off-by: Baoquan He <baoquan.he@linux.dev>
---
 mm/swapfile.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/mm/swapfile.c b/mm/swapfile.c
index da085a75d7b5..8bf336678887 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -58,6 +58,8 @@ static void move_cluster(struct swap_info_struct *si,
 			 enum swap_cluster_flags new_flags);
 #ifdef CONFIG_ZSWAP
 #define GHOST_MAX_CLUSTER_BYTES (64UL * 1024 * 1024)
+static int swap_ghost_extend_max(struct swap_info_struct *si,
+				 unsigned int new_maxpages);
 #endif
 
 /*
@@ -4247,6 +4249,114 @@ swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset)
 
 	return (swp_entry_t){};
 }
+
+/*
+ * swap_ghost_extend_max — Extend an active ghost swap device.
+ * @si:  ghost device (SWP_GHOST set)
+ * @new: new max pages (> si->max)
+ *
+ * Allocates physical pages and maps them at the end of the reserved
+ * sparse vmalloc area.  si->cluster_info never moves.  New clusters
+ * are fully initialized before si->max is published via WRITE_ONCE.
+ *
+ * Returns 0, -ENOMEM, -EINVAL, or -E2BIG.  Caller holds swapon_mutex.
+ */
+static int swap_ghost_extend_max(struct swap_info_struct *si,
+				 unsigned int new)
+{
+	unsigned long old_max = si->max, old_nr, new_nr;
+	unsigned long old_bytes, new_bytes, nr_new_pg;
+	struct page **new_pglist;
+	unsigned long i;
+
+	if (!(si->flags & SWP_GHOST) ||
+	    !(si->flags & (SWP_USED | SWP_WRITEOK)) ||
+	    new <= si->max)
+		return -EINVAL;
+
+	old_nr = DIV_ROUND_UP(si->max, SWAPFILE_CLUSTER);
+	new_nr = DIV_ROUND_UP(new, SWAPFILE_CLUSTER);
+	old_bytes = ALIGN(old_nr * sizeof(struct swap_cluster_info), PAGE_SIZE);
+	new_bytes = ALIGN(new_nr * sizeof(struct swap_cluster_info), PAGE_SIZE);
+	nr_new_pg = (new_bytes - old_bytes) / PAGE_SIZE;
+
+	if (old_bytes + nr_new_pg * PAGE_SIZE > si->cluster_vm->size) {
+		pr_warn("ghost: extend to %u exceeds reserved VA (%lu bytes)\n",
+			new, si->cluster_vm->size);
+		return -E2BIG;
+	}
+
+	new_pglist = kvmalloc_array(si->cluster_vm->nr_pages + nr_new_pg,
+				    sizeof(struct page *), GFP_KERNEL);
+	if (!new_pglist)
+		return -ENOMEM;
+	if (si->cluster_vm->pages) {
+		memcpy(new_pglist, si->cluster_vm->pages,
+		       si->cluster_vm->nr_pages * sizeof(struct page *));
+		kvfree(si->cluster_vm->pages);
+	}
+	si->cluster_vm->pages = (void *)new_pglist;
+
+	for (i = 0; i < nr_new_pg; i++) {
+		struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+		if (!page) {
+			while (i-- > 0)
+				__free_page(new_pglist[si->cluster_vm->nr_pages + i]);
+			return -ENOMEM;
+		}
+		new_pglist[si->cluster_vm->nr_pages + i] = page;
+	}
+
+	if (vm_area_map_pages(si->cluster_vm,
+			      (unsigned long)si->cluster_vm->addr + old_bytes,
+			      (unsigned long)si->cluster_vm->addr + new_bytes,
+			      &new_pglist[si->cluster_vm->nr_pages])) {
+		for (i = 0; i < nr_new_pg; i++)
+			__free_page(new_pglist[si->cluster_vm->nr_pages + i]);
+		return -ENOMEM;
+	}
+	si->cluster_vm->nr_pages += nr_new_pg;
+
+	for (i = old_nr; i < new_nr; i++) {
+		struct swap_cluster_info *ci = &si->cluster_info[i];
+		spin_lock_init(&ci->lock);
+		INIT_LIST_HEAD(&ci->list);
+	}
+
+	if (new % SWAPFILE_CLUSTER) {
+		unsigned long base = (new - 1) / SWAPFILE_CLUSTER *
+				    SWAPFILE_CLUSTER;
+		unsigned long idx = base / SWAPFILE_CLUSTER;
+		struct swap_cluster_info *ci = &si->cluster_info[idx];
+
+		if (!ci->count && swap_cluster_alloc_table(ci, GFP_KERNEL)) {
+			for (i = 0; i < nr_new_pg; i++)
+				__free_page(new_pglist[si->cluster_vm->nr_pages + i]);
+			return -ENOMEM;
+		}
+		spin_lock(&ci->lock);
+		for (i = new % SWAPFILE_CLUSTER; i < SWAPFILE_CLUSTER; i++)
+			__swap_table_set(ci, i, SWP_TB_BAD);
+		spin_unlock(&ci->lock);
+	}
+
+	WRITE_ONCE(si->max, new);
+
+	for (i = old_nr; i < new_nr; i++) {
+		struct swap_cluster_info *ci = &si->cluster_info[i];
+		ci->flags = CLUSTER_FLAG_FREE;
+		spin_lock(&si->lock);
+		list_add_tail(&ci->list, &si->free_clusters);
+		spin_unlock(&si->lock);
+	}
+
+	si->pages += new - old_max;
+	atomic_long_add(new - old_max, &nr_swap_pages);
+	spin_lock(&swap_lock);
+	total_swap_pages += new - old_max;
+	spin_unlock(&swap_lock);
+	return 0;
+}
 #endif /* CONFIG_ZSWAP */
 
 static int __init swapfile_init(void)
-- 
2.54.0


  parent reply	other threads:[~2026-07-07  8:27 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-07  8:26 [RFC PATCH 00/10] mm/swap: ghost swapfile with backend switching via Redirect entries Baoquan He
2026-07-07  8:26 ` [RFC PATCH 01/10] mm: ghost swapfile support for zswap Baoquan He
2026-07-07  8:26 ` [RFC PATCH 02/10] mm/swap_table: add Redirect entry encoding for ghost swap backend switching Baoquan He
2026-07-07  8:26 ` [RFC PATCH 03/10] mm/swap: add redirect_xa field and ghost redirect helper declarations Baoquan He
2026-07-07  8:26 ` [RFC PATCH 04/10] mm/swapfile: implement ghost redirect helpers and free-path cascade Baoquan He
2026-07-07  8:26 ` [RFC PATCH 05/10] mm/swap_state: restore Redirect entry when swap cache folio is removed Baoquan He
2026-07-07  8:26 ` [RFC PATCH 06/10] mm/zswap: implement ghost-to-physical writeback for backend switching Baoquan He
2026-07-07  8:26 ` [RFC PATCH 07/10] mm/page_io: forward ghost swap reads to physical device via Redirect Baoquan He
2026-07-07  8:26 ` [RFC PATCH 08/10] mm/swapfile: manage ghost cluster_info via lazy vmalloc Baoquan He
2026-07-07  8:26 ` Baoquan He [this message]
2026-07-07 22:36   ` [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth Nhat Pham
2026-07-09 14:52     ` Baoquan He
2026-07-10  1:11       ` Nhat Pham
2026-07-11  9:02   ` Chris Li
2026-07-07  8:26 ` [RFC PATCH 10/10] mm/swapfile: add sysfs interface for ghost swap extension Baoquan He
2026-07-07  8:56 ` [RFC PATCH 00/10] mm/swap: ghost swapfile with backend switching via Redirect entries Baoquan He
2026-07-07 21:23 ` Nhat Pham
2026-07-07 21:25   ` Nhat Pham
2026-07-09 11:38   ` Baoquan He
2026-07-13  0:02     ` Nhat Pham
2026-07-13 17:47       ` Chris Li
2026-07-11  8:49 ` Chris Li

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=20260707082614.95030-10-baoquan.he@linux.dev \
    --to=baoquan.he@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=baohua@kernel.org \
    --cc=chengming.zhou@linux.dev \
    --cc=chrisl@kernel.org \
    --cc=hannes@cmpxchg.org \
    --cc=kasong@tencent.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=nphamcs@gmail.com \
    --cc=shikemeng@huaweicloud.com \
    --cc=yosry@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.