[PATCH 5/6] mm, swap: do not retake the cluster lock when uncharging an xswap slot
From: Baoquan He
Date: Fri Oct 02 2026 - 21:13:28 EST
xswap_release_slot_backend() runs with ci->lock held. Its callers free
the slots of a cluster they have locked. It called
xswap_backend_uncharge() to give the swap charge back, and that takes
the same cluster lock again to read the owner out of the swap table.
A spinlock is not recursive, so it spins forever. The swapin path hangs
on it:
do_swap_page
folio_free_swap
swap_cache_del_folio
__swap_cluster_free_entries
xswap_release_slot_backend
xswap_backend_uncharge <- spins here
Read the owner from ci directly instead. The lock is already held, and
the cgroup id is still in the swap table at this point: the loop clears
it after this call.
xswap_backend_uncharge() stays for xswap_backend_free(), which runs
without the cluster lock.
Signed-off-by: Baoquan He <hebaoquan@xxxxxxxxxx>
---
mm/swapfile.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/mm/swapfile.c b/mm/swapfile.c
index a3d4561566eb..419d19f3b98c 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -3313,6 +3313,7 @@ static void xswap_release_slot_backend(struct swap_info_struct *si,
{
swp_entry_t entry = swp_entry(si->type, cluster_offset(si, ci) + slot);
struct swap_info_struct *psi;
+ unsigned short id = xswap_entry_id(ci, slot);
swp_entry_t phys = { .val = ci->xs_table[slot] };
WRITE_ONCE(ci->xs_table[slot], 0);
@@ -3321,7 +3322,13 @@ static void xswap_release_slot_backend(struct swap_info_struct *si,
if (psi)
xswap_free_phys_slot(psi, entry, phys);
- xswap_backend_uncharge(entry, 1);
+ /*
+ * ci->lock is held here, so read the owner out of the swap table
+ * directly. xswap_backend_uncharge() would take the same lock
+ * again, and a spinlock is not recursive.
+ */
+ if (id)
+ mem_cgroup_swap_uncharge(id, 1);
}
/*
--
2.54.0