From: Baoquan He <baoquan.he@linux.dev>
To: linux-mm@kvack.org
Cc: chrisl@kernel.org, nphamcs@gmail.com, kasong@tencent.com,
baohua@kernel.org, youngjun.park@lge.com, hannes@cmpxchg.org,
yosry@kernel.org, david@kernel.org, shikemeng@huaweicloud.com,
chengming.zhou@linux.dev, linux-kernel@vger.kernel.org,
Baoquan He <baoquan.he@linux.dev>
Subject: [RFC PATCH v2 06/10] mm, swap: free backing pages in xswap_unmap_clusters
Date: Wed, 5 Aug 2026 15:53:29 +0800 [thread overview]
Message-ID: <20260805075336.3579395-7-baoquan.he@linux.dev> (raw)
In-Reply-To: <20260805075336.3579395-1-baoquan.he@linux.dev>
vm_area_unmap_pages() only clears PTEs and frees intermediate page
table pages — it does not free the backing physical pages allocated
by xswap_map_clusters().
Fix this by walking the page table with apply_to_existing_page_range()
before the unmap to collect all struct pages in the range. After
vunmap_range() clears the PTEs, free the collected pages via
__free_page().
Use a simple xswap_page_data collector callback: for each present PTE,
collect pte_page() into a dynamically allocated array. The array is
freed after the pages are released.
Signed-off-by: Baoquan He <baoquan.he@linux.dev>
---
mm/swapfile.c | 52 +++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 46 insertions(+), 6 deletions(-)
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 0d1d0c5e24c6..c43c8746378e 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -3796,6 +3796,23 @@ static int xswap_map_clusters(struct swap_info_struct *si,
return -ENOMEM;
}
+struct xswap_page_data {
+ struct page **pages;
+ int nr;
+ int max;
+};
+
+static int xswap_collect_page(pte_t *pte, unsigned long addr, void *data)
+{
+ struct xswap_page_data *xpd = data;
+
+ if (!pte_present(*pte))
+ return 0;
+ if (xpd->nr < xpd->max)
+ xpd->pages[xpd->nr++] = pte_page(*pte);
+ return 0;
+}
+
static void xswap_unmap_clusters(struct swap_info_struct *si,
unsigned long start_idx, unsigned long nr)
{
@@ -3805,11 +3822,15 @@ static void xswap_unmap_clusters(struct swap_info_struct *si,
/*
* Round to page boundaries: start up (skip partial page that may
* contain clusters still in use before start_idx), end up so the
- * entire range is covered. vm_area_unmap_pages() operates on
- * whole pages.
+ * entire range is covered. vm_area_unmap_pages() and
+ * apply_to_existing_page_range() operate on whole pages.
*/
unsigned long vm_start = PAGE_ALIGN(start_addr);
unsigned long vm_end = PAGE_ALIGN(end_addr);
+ unsigned long size;
+ unsigned long npages;
+ struct xswap_page_data xpd;
+ int i;
mutex_lock(&si->xswap_lock);
@@ -3818,12 +3839,31 @@ static void xswap_unmap_clusters(struct swap_info_struct *si,
goto out;
}
- vm_area_unmap_pages(si->cluster_vm, vm_start, vm_end);
+ size = vm_end - vm_start;
+ npages = size >> PAGE_SHIFT;
+
/*
- * vm_area_unmap_pages() only clears PTEs; it does not free the
- * physical pages. Walk the page table to find and free them.
+ * Walk the page table to collect physical pages before unmapping.
+ * vm_area_unmap_pages() only clears PTEs and frees intermediate
+ * page table pages — it does not free backing pages.
*/
- /* TODO: free backing pages via page table walk or tracking bitmap */
+ xpd.pages = kmalloc_array(npages, sizeof(*xpd.pages), GFP_KERNEL);
+ if (xpd.pages) {
+ xpd.nr = 0;
+ xpd.max = npages;
+ apply_to_existing_page_range(&init_mm, vm_start, size,
+ xswap_collect_page, &xpd);
+ }
+
+ vm_area_unmap_pages(si->cluster_vm, vm_start, vm_end);
+
+ /* Free the collected backing pages */
+ if (xpd.pages) {
+ for (i = 0; i < xpd.nr; i++)
+ __free_page(xpd.pages[i]);
+ kfree(xpd.pages);
+ }
+
mutex_unlock(&si->xswap_lock);
out:
--
2.54.0
next prev parent reply other threads:[~2026-08-05 7:54 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-05 7:53 [RFC PATCH v2 00/10] mm, swap: dynamic cluster management for xswap devices Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 01/10] mm: xswap support for zswap Baoquan He
2026-08-05 14:17 ` Johannes Weiner
2026-08-07 9:11 ` Baoquan He
2026-08-10 1:49 ` Youngjun Park
2026-08-10 7:25 ` Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 02/10] mm, swap: add CONFIG_XSWAP and xswap fields to swap_info_struct Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 03/10] mm, swap: add xswap cluster grow via VM_SPARSE vmalloc Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 04/10] mm, swap: add xswap grow trigger on cluster allocation Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 05/10] mm, swap: add xswap_try_shrink and shrink trigger on cluster free Baoquan He
2026-08-05 7:53 ` Baoquan He [this message]
2026-08-05 7:53 ` [RFC PATCH v2 07/10] mm, swap: add nr_free_tail for O(1) xswap shrink detection Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 08/10] mm, swap: add adjustable runtime ceiling (nr_clusters) for xswap Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 09/10] mm, swap: add debugfs knob for xswap per-device cluster limit Baoquan He
2026-08-05 7:53 ` [RFC PATCH v2 10/10] mm, swap: defer xswap shrink to workqueue to avoid lock recursion Baoquan He
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=20260805075336.3579395-7-baoquan.he@linux.dev \
--to=baoquan.he@linux.dev \
--cc=baohua@kernel.org \
--cc=chengming.zhou@linux.dev \
--cc=chrisl@kernel.org \
--cc=david@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.