]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mm, swap: avoid BUG_ON in relocate_cluster()
authorKemeng Shi <shikemeng@huaweicloud.com>
Sat, 22 Feb 2025 16:08:47 +0000 (00:08 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 6 Mar 2025 05:36:15 +0000 (21:36 -0800)
If allocation is racy with swapoff, we may call free_cluster for cluster
already in free list and trigger BUG_ON() as following:

Allocation                        Swapoff
cluster_alloc_swap_entry
 ...
 /* may get a free cluster with offset */
 offset = xxx;
 if (offset)
  ci = lock_cluster(si, offset);

                                  ...
                                   del_from_avail_list(p, true);
                                    si->flags &= ~SWP_WRITEOK;

  alloc_swap_scan_cluster(si, ci, ...)
   ...
   /* failed to alloc entry from free entry */
   if (!cluster_alloc_range(...))
    break;
   ...
   /* add back a free cluster */
   relocate_cluster(si, ci);
    if (!ci->count)
     free_cluster(si, ci);
      VM_BUG_ON(ci->flags == CLUSTER_FLAG_FREE);

To prevent the BUG_ON(), call free_cluster() for free cluster to move the
cluster to tail of list.

Check cluster is not free before calling free_cluster() in
relocate_cluster() to avoid BUG_ON().

Link: https://lkml.kernel.org/r/20250222160850.505274-4-shikemeng@huaweicloud.com
Fixes: 3b644773eefd ("mm, swap: reduce contention on device lock")
Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com>
Reviewed-by: Kairui Song <kasong@tencent.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/swapfile.c

index 6460b6cb36c991cdacbb8d9a33aee94934b01bd2..df7c4e8b089ca513c1f76a03f7224b822c6b1cea 100644 (file)
@@ -653,7 +653,8 @@ static void relocate_cluster(struct swap_info_struct *si,
                return;
 
        if (!ci->count) {
-               free_cluster(si, ci);
+               if (ci->flags != CLUSTER_FLAG_FREE)
+                       free_cluster(si, ci);
        } else if (ci->count != SWAPFILE_CLUSTER) {
                if (ci->flags != CLUSTER_FLAG_FRAG)
                        move_cluster(si, ci, &si->frag_clusters[ci->order],