[PATCH] mm:zswap: fix zswap entry reclamation failure in two scenarios

From: Zhongkun He
Date: Mon Nov 13 2023 - 08:06:21 EST


I recently found two scenarios where zswap entry could not be
released, which will cause shrink_worker and active recycling
to fail.
1)The swap entry has been freed, but cached in swap_slots_cache,
no swap cache and swapcount=0.
2)When the option zswap_exclusive_loads_enabled disabled and
zswap_load completed(page in swap_cache and swapcount = 0).

The above two cases need to be determined by swapcount=0,
fix it.

Signed-off-by: Zhongkun He <hezhongkun.hzk@xxxxxxxxxxxxx>
---
mm/zswap.c | 35 +++++++++++++++++++++++++----------
1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/mm/zswap.c b/mm/zswap.c
index 74411dfdad92..db95491bcdd5 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1063,11 +1063,12 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
struct mempolicy *mpol;
struct scatterlist input, output;
struct crypto_acomp_ctx *acomp_ctx;
+ struct swap_info_struct *si;
struct zpool *pool = zswap_find_zpool(entry);
bool page_was_allocated;
u8 *src, *tmp = NULL;
unsigned int dlen;
- int ret;
+ int ret = 0;
struct writeback_control wbc = {
.sync_mode = WB_SYNC_NONE,
};
@@ -1082,16 +1083,30 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
mpol = get_task_policy(current);
page = __read_swap_cache_async(swpentry, GFP_KERNEL, mpol,
NO_INTERLEAVE_INDEX, &page_was_allocated);
- if (!page) {
+ if (!page)
ret = -ENOMEM;
- goto fail;
- }
-
- /* Found an existing page, we raced with load/swapin */
- if (!page_was_allocated) {
+ else if (!page_was_allocated) {
+ /* Found an existing page, we raced with load/swapin */
put_page(page);
ret = -EEXIST;
- goto fail;
+ }
+
+ if (ret) {
+ si = get_swap_device(swpentry);
+ if (!si)
+ goto out;
+
+ /* Two cases to directly release zswap_entry.
+ * 1) -ENOMEM,if the swpentry has been freed, but cached in
+ * swap_slots_cache(no page and swapcount = 0).
+ * 2) -EEXIST, option zswap_exclusive_loads_enabled disabled and
+ * zswap_load completed(page in swap_cache and swapcount = 0).
+ */
+ if (!swap_swapcount(si, swpentry))
+ ret = 0;
+
+ put_swap_device(si);
+ goto out;
}

/*
@@ -1106,7 +1121,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
spin_unlock(&tree->lock);
delete_from_swap_cache(page_folio(page));
ret = -ENOMEM;
- goto fail;
+ goto out;
}
spin_unlock(&tree->lock);

@@ -1151,7 +1166,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry,

return ret;

-fail:
+out:
if (!zpool_can_sleep_mapped(pool))
kfree(tmp);

--
2.25.1