]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mm/swapfile: fix list iteration when next node is removed during discard
authorYoungjun Park <youngjun.park@lge.com>
Thu, 27 Nov 2025 10:03:02 +0000 (19:03 +0900)
committerAndrew Morton <akpm@linux-foundation.org>
Sat, 29 Nov 2025 18:41:10 +0000 (10:41 -0800)
Patch series "mm/swapfile: fix and cleanup swap list iterations", v2.

This series fixes a potential list iteration issue in swap_sync_discard()
when devices are removed, and includes a cleanup for
__folio_throttle_swaprate().

This patch (of 2):

When the next node is removed from the plist (e.g.  by swapoff),
plist_del() makes the node point to itself, causing the iteration to loop
on the same entry indefinitely.

Add a plist_node_empty() check to detect this case and restart iteration,
allowing swap_sync_discard() to continue processing remaining swap devices
that still have pending discard entries.

Additionally, switch from swap_avail_lock/swap_avail_head to
swap_lock/swap_active_head so that iteration is only affected by swapoff
operations rather than frequent availability changes, reducing exceptional
condition checks and lock contention.

Link: https://lkml.kernel.org/r/20251127100303.783198-1-youngjun.park@lge.com
Link: https://lkml.kernel.org/r/20251127100303.783198-2-youngjun.park@lge.com
Fixes: 686ea517f471 ("mm, swap: do not perform synchronous discard during allocation")
Signed-off-by: Youngjun Park <youngjun.park@lge.com>
Suggested-by: Kairui Song <kasong@tencent.com>
Acked-by: Kairui Song <kasong@tencent.com>
Reviewed-by: Baoquan He <bhe@redhat.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Chris Li <chrisl@kernel.org>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Nhat Pham <nphamcs@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/swapfile.c

index d12332423a06215f0cfa9c650e46164540817c9f..8116f36e440bc828495564df25824ff673a06ef4 100644 (file)
@@ -1387,9 +1387,10 @@ static bool swap_sync_discard(void)
        bool ret = false;
        struct swap_info_struct *si, *next;
 
-       spin_lock(&swap_avail_lock);
-       plist_for_each_entry_safe(si, next, &swap_avail_head, avail_list) {
-               spin_unlock(&swap_avail_lock);
+       spin_lock(&swap_lock);
+start_over:
+       plist_for_each_entry_safe(si, next, &swap_active_head, list) {
+               spin_unlock(&swap_lock);
                if (get_swap_device_info(si)) {
                        if (si->flags & SWP_PAGE_DISCARD)
                                ret = swap_do_scheduled_discard(si);
@@ -1397,9 +1398,12 @@ static bool swap_sync_discard(void)
                }
                if (ret)
                        return true;
-               spin_lock(&swap_avail_lock);
+
+               spin_lock(&swap_lock);
+               if (plist_node_empty(&next->list))
+                       goto start_over;
        }
-       spin_unlock(&swap_avail_lock);
+       spin_unlock(&swap_lock);
 
        return false;
 }