]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - mm/swapfile.c
mm: swap: fix race between free_swap_and_cache() and swapoff()
[thirdparty/linux.git] / mm / swapfile.c
index 2b3a2d85e350ba1021f627453ee62ed96478af2c..1155a630411927ce29079dcbf79a193a30d78694 100644 (file)
@@ -1232,6 +1232,11 @@ static unsigned char __swap_entry_free_locked(struct swap_info_struct *p,
  * with get_swap_device() and put_swap_device(), unless the swap
  * functions call get/put_swap_device() by themselves.
  *
+ * Note that when only holding the PTL, swapoff might succeed immediately
+ * after freeing a swap entry. Therefore, immediately after
+ * __swap_entry_free(), the swap info might become stale and should not
+ * be touched without a prior get_swap_device().
+ *
  * Check whether swap entry is valid in the swap device.  If so,
  * return pointer to swap_info_struct, and keep the swap entry valid
  * via preventing the swap device from being swapoff, until
@@ -1609,13 +1614,19 @@ int free_swap_and_cache(swp_entry_t entry)
        if (non_swap_entry(entry))
                return 1;
 
-       p = _swap_info_get(entry);
+       p = get_swap_device(entry);
        if (p) {
+               if (WARN_ON(data_race(!p->swap_map[swp_offset(entry)]))) {
+                       put_swap_device(p);
+                       return 0;
+               }
+
                count = __swap_entry_free(p, entry);
                if (count == SWAP_HAS_CACHE &&
                    !swap_page_trans_huge_swapped(p, entry))
                        __try_to_reclaim_swap(p, swp_offset(entry),
                                              TTRS_UNMAPPED | TTRS_FULL);
+               put_swap_device(p);
        }
        return p != NULL;
 }