From: Baoquan He <baoquan.he@linux.dev>
To: linux-mm@kvack.org
Cc: akpm@linux-foundation.org, hannes@cmpxchg.org, yosry@kernel.org,
nphamcs@gmail.com, chengming.zhou@linux.dev, chrisl@kernel.org,
kasong@tencent.com, shikemeng@huaweicloud.com, baohua@kernel.org,
youngjun.park@lge.com, linux-kernel@vger.kernel.org,
Baoquan He <baoquan.he@linux.dev>
Subject: [RFC PATCH 5/7] mm/swapfile: handle Pointer entries in count/dup/put paths
Date: Tue, 7 Jul 2026 15:32:02 +0800 [thread overview]
Message-ID: <20260707073215.72183-6-baoquan.he@linux.dev> (raw)
In-Reply-To: <20260707073215.72183-1-baoquan.he@linux.dev>
Update swap count, duplicate, and put operations to handle Pointer-type
swap table entries where the count is stored in zswap_entry->swp_tb_val
rather than inline in the entry bits.
Signed-off-by: Baoquan He <baoquan.he@linux.dev>
---
mm/swapfile.c | 95 ++++++++++++++++++++++++++++++++++++++++-----------
mm/zswap.c | 5 +++
2 files changed, 81 insertions(+), 19 deletions(-)
diff --git a/mm/swapfile.c b/mm/swapfile.c
index a602e5820513..15572383cafa 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1536,25 +1536,51 @@ static void __swap_cluster_put_entry(struct swap_cluster_info *ci,
lockdep_assert_held(&ci->lock);
swp_tb = __swap_table_get(ci, ci_off);
+
+ /* Pointer entries store count in the zswap_entry struct */
+ if (swp_tb_is_pointer(swp_tb)) {
+ zswap_swp_tb_put_count(swp_tb, ci, ci_off);
+ return;
+ }
+
count = __swp_tb_get_count(swp_tb);
VM_WARN_ON_ONCE(count <= 0);
VM_WARN_ON_ONCE(count > SWP_TB_COUNT_MAX);
-
- if (count == SWP_TB_COUNT_MAX) {
- count = ci->extend_table[ci_off];
- /* Overflow starts with SWP_TB_COUNT_MAX */
- VM_WARN_ON_ONCE(count < SWP_TB_COUNT_MAX);
- count--;
- if (count == (SWP_TB_COUNT_MAX - 1)) {
- ci->extend_table[ci_off] = 0;
- __swap_table_set(ci, ci_off, __swp_tb_mk_count(swp_tb, count));
+ if (count == SWP_TB_COUNT_MAX) {
+ if (!ci->extend_table) {
+ /*
+ * MAX without extend_table: MAX is the real
+ * count. Decrement and write back inline.
+ */
+ count--;
+ __swap_table_set(ci, ci_off,
+ __swp_tb_mk_count(swp_tb, count));
+ } else {
+ /*
+ * MAX with extend_table: real count lives in
+ * ci->extend_table[ci_off]. Pull, decrement
+ * it, and either move it back inline (if now
+ * fits) or write it back to the extend table.
+ */
+ count = ci->extend_table[ci_off];
+ /* Overflow starts with SWP_TB_COUNT_MAX */
+ VM_WARN_ON_ONCE(count < SWP_TB_COUNT_MAX);
+ count--;
+ if (count == (SWP_TB_COUNT_MAX - 1)) {
+ ci->extend_table[ci_off] = 0;
+ __swap_table_set(ci, ci_off,
+ __swp_tb_mk_count(swp_tb, count));
+ } else {
+ ci->extend_table[ci_off] = count;
+ }
+ }
} else {
- ci->extend_table[ci_off] = count;
+ /* count < MAX: simple inline decrement */
+ count--;
+ __swap_table_set(ci, ci_off,
+ __swp_tb_mk_count(swp_tb, count));
}
- } else {
- __swap_table_set(ci, ci_off, __swp_tb_mk_count(swp_tb, --count));
- }
/*
* `SWP_TB_COUNT_MAX - 1` triggers extend table allocation. If the
@@ -1596,7 +1622,18 @@ static void swap_put_entries_cluster(struct swap_info_struct *si,
ci_end = ci_off + nr;
do {
swp_tb = __swap_table_get(ci, ci_off);
- if (swp_tb_get_count(swp_tb) == 1) {
+ /*
+ * Pointer entries store count in the zswap_entry;
+ * handle them before swp_tb_get_count (which returns
+ * -EINVAL for non-countable types).
+ */
+ if (swp_tb_is_pointer(swp_tb)) {
+ if (zswap_swp_tb_get_count(swp_tb) == 1) {
+ if (ci_batch == -1)
+ ci_batch = ci_off;
+ continue;
+ }
+ } else if (swp_tb_get_count(swp_tb) == 1) {
/* count == 1 and non-cached slots will be batch freed. */
if (!swp_tb_is_folio(swp_tb)) {
if (ci_batch == -1)
@@ -1647,6 +1684,11 @@ static int __swap_cluster_dup_entry(struct swap_cluster_info *ci,
/* Bad or special slots can't be handled */
if (WARN_ON_ONCE(swp_tb_is_bad(swp_tb)))
return -EINVAL;
+
+ /* Pointer entries store count in the zswap_entry struct */
+ if (swp_tb_is_pointer(swp_tb))
+ return zswap_swp_tb_dup_count(swp_tb, ci, ci_off);
+
count = __swp_tb_get_count(swp_tb);
/* Must be either cached or have a count already */
if (WARN_ON_ONCE(!count && !swp_tb_is_folio(swp_tb)))
@@ -1923,13 +1965,19 @@ void __swap_cluster_free_entries(struct swap_info_struct *si,
old_tb = __swap_table_get(ci, ci_off);
/*
* Freeing is done after release of the last swap count
- * ref, or after swap cache is dropped
+ * ref, or after swap cache is dropped. A Pointer entry
+ * means zswap has a compressed copy; the xarray still
+ * holds the entry and zswap_invalidate will free it.
*/
- VM_WARN_ON(!swp_tb_is_shadow(old_tb) || __swp_tb_get_count(old_tb) > 1);
+ if (swp_tb_is_pointer(old_tb))
+ VM_WARN_ON(zswap_swp_tb_get_count(old_tb) > 1);
+ else
+ VM_WARN_ON(!swp_tb_is_shadow(old_tb) ||
+ __swp_tb_get_count(old_tb) > 1);
/* Resetting the slot to NULL also clears the inline flags. */
__swap_table_set(ci, ci_off, null_to_swp_tb());
- if (!SWAP_TABLE_HAS_ZEROFLAG)
+ if (!SWAP_TABLE_HAS_ZEROFLAG && !swp_tb_is_pointer(old_tb))
__swap_table_clear_zero(ci, ci_off);
/*
@@ -1961,8 +2009,11 @@ int __swap_count(swp_entry_t entry)
{
struct swap_cluster_info *ci = __swap_entry_to_cluster(entry);
unsigned int ci_off = swp_cluster_offset(entry);
+ unsigned long swp_tb = __swap_table_get(ci, ci_off);
- return swp_tb_get_count(__swap_table_get(ci, ci_off));
+ if (swp_tb_is_pointer(swp_tb))
+ return zswap_swp_tb_get_count(swp_tb);
+ return swp_tb_get_count(swp_tb);
}
/**
@@ -1980,6 +2031,8 @@ bool swap_entry_swapped(struct swap_info_struct *si, swp_entry_t entry)
swp_tb = swap_table_get(ci, offset % SWAPFILE_CLUSTER);
swap_cluster_unlock(ci);
+ if (swp_tb_is_pointer(swp_tb))
+ return zswap_swp_tb_get_count(swp_tb) > 0;
return swp_tb_get_count(swp_tb) > 0;
}
@@ -2556,8 +2609,12 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
if (!folio) {
swp_tb = swap_table_get(__swap_entry_to_cluster(entry),
swp_cluster_offset(entry));
- if (swp_tb_get_count(swp_tb) <= 0)
+ if (swp_tb_is_pointer(swp_tb)) {
+ if (zswap_swp_tb_get_count(swp_tb) <= 0)
+ continue;
+ } else if (swp_tb_get_count(swp_tb) <= 0) {
continue;
+ }
return -ENOMEM;
}
diff --git a/mm/zswap.c b/mm/zswap.c
index 1bb484d1ac87..58259673ce2b 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1774,6 +1774,11 @@ void zswap_swp_tb_put_count(unsigned long swp_tb,
VM_WARN_ON_ONCE(count == 0);
if (count == SWP_TB_COUNT_MAX) {
+ if (!ci->extend_table) {
+ count--;
+ entry->swp_tb_val = __swp_tb_mk_count(entry->swp_tb_val, count);
+ return;
+ }
count = ci->extend_table[ci_off];
/* Overflow starts with SWP_TB_COUNT_MAX */
VM_WARN_ON_ONCE(count < SWP_TB_COUNT_MAX);
--
2.54.0
next prev parent reply other threads:[~2026-07-07 7:33 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-07 7:31 [RFC PATCH 0/7] mm: store zswap entries in swap table (Pointer entry) Baoquan He
2026-07-07 7:31 ` [RFC PATCH 1/7] mm/swap_table: add Pointer type support for zswap entry storage Baoquan He
2026-07-07 7:31 ` [RFC PATCH 2/7] mm/zswap: add swp_tb_val to zswap_entry and Pointer accessor helpers Baoquan He
2026-07-07 7:32 ` [RFC PATCH 3/7] mm/swap_state: do zswap conversion in __swap_cache_do_del_folio Baoquan He
2026-07-07 7:32 ` [RFC PATCH 4/7] mm/zswap, mm/swap_state: handle Pointer entries in add_folio path Baoquan He
2026-07-07 7:32 ` Baoquan He [this message]
2026-07-07 7:32 ` [RFC PATCH 6/7] mm/zswap, mm/swap_state: migrate zswap to use swap_table for entry indexing Baoquan He
2026-07-07 7:32 ` [RFC PATCH 7/7] mm/zswap, mm/swap_state: replace xarray indexing with swap table lookups Baoquan He
2026-07-07 19:27 ` [RFC PATCH 0/7] mm: store zswap entries in swap table (Pointer entry) Nhat Pham
2026-07-07 19:49 ` Yosry Ahmed
2026-07-07 22:17 ` Nhat Pham
2026-07-07 21:32 ` [syzbot ci] " syzbot ci
2026-07-13 15:25 ` [RFC PATCH 0/7] " 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=20260707073215.72183-6-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.