When a swap cache folio is removed from a ghost swap device, __swap_cache_do_del_folio() normally restores a Shadow entry with the preserved swap count. For ghost devices, the entry may have originated from a Redirect (zswap writeback to a physical device). Check the redirect_xa before restoring a Shadow: if a redirect mapping exists, restore the Redirect entry instead. This ensures future swapins are correctly forwarded to the physical device. The redirect mapping is stored in the xarray during zswap writeback (see zswap_writeback_ghost()) and persists across the swap-cache PFN lifecycle. Signed-off-by: Baoquan He --- mm/swap_state.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mm/swap_state.c b/mm/swap_state.c index 5fb893cc7fce..cb2ed2a18f9f 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -329,6 +330,26 @@ static void __swap_cache_do_del_folio(struct swap_cluster_info *ci, folio_swapped = true; else need_free = true; + /* + * For ghost swap devices with a redirect mapping, + * restore the Redirect entry so future swapins + * are forwarded to the physical device. + */ + if (si->flags & SWP_GHOST) { + swp_entry_t phys_entry; + void *val; + + val = xa_load(&si->redirect_xa, + (swp_offset(entry) - + swp_cluster_offset(entry)) + + ci_off); + if (val && !xa_is_err(val)) { + phys_entry.val = xa_to_value(val); + __swap_table_set(ci, ci_off, + swp_entry_to_redirect(phys_entry)); + continue; + } + } /* If shadow is NULL, we set an empty shadow. */ __swap_table_set(ci, ci_off, shadow_to_swp_tb(shadow, __swp_tb_get_flags(old_tb))); -- 2.54.0