}
}
+/**
+ * __swap_cache_prepare_and_add - Prepare the folio and add it to swap cache.
+ * @entry: swap entry to be bound to the folio.
+ * @folio: folio to be added.
+ * @gfp: memory allocation flags for charge, can be 0 if @charged if true.
+ * @charged: if the folio is already charged.
+ * @skip_if_exists: if the slot is in a cached state, return NULL.
+ * This is an old workaround that will be removed shortly.
+ *
+ * Update the swap_map and add folio as swap cache, typically before swapin.
+ * All swap slots covered by the folio must have a non-zero swap count.
+ *
+ * Context: Caller must protect the swap device with reference count or locks.
+ * Return: Returns the folio being added on success. Returns the existing folio
+ * if @entry is already cached. Returns NULL if raced with swapin or swapoff.
+ */
+static struct folio *__swap_cache_prepare_and_add(swp_entry_t entry,
+ struct folio *folio,
+ gfp_t gfp, bool charged,
+ bool skip_if_exists)
+{
+ struct folio *swapcache;
+ void *shadow;
+ int ret;
+
+ /*
+ * Check and pin the swap map with SWAP_HAS_CACHE, then add the folio
+ * into the swap cache. Loop with a schedule delay if raced with
+ * another process setting SWAP_HAS_CACHE. This hackish loop will
+ * be fixed very soon.
+ */
+ for (;;) {
+ ret = swapcache_prepare(entry, folio_nr_pages(folio));
+ if (!ret)
+ break;
+
+ /*
+ * The skip_if_exists is for protecting against a recursive
+ * call to this helper on the same entry waiting forever
+ * here because SWAP_HAS_CACHE is set but the folio is not
+ * in the swap cache yet. This can happen today if
+ * mem_cgroup_swapin_charge_folio() below triggers reclaim
+ * through zswap, which may call this helper again in the
+ * writeback path.
+ *
+ * Large order allocation also needs special handling on
+ * race: if a smaller folio exists in cache, swapin needs
+ * to fallback to order 0, and doing a swap cache lookup
+ * might return a folio that is irrelevant to the faulting
+ * entry because @entry is aligned down. Just return NULL.
+ */
+ if (ret != -EEXIST || skip_if_exists || folio_test_large(folio))
+ return NULL;
+
+ /*
+ * Check the swap cache again, we can only arrive
+ * here because swapcache_prepare returns -EEXIST.
+ */
+ swapcache = swap_cache_get_folio(entry);
+ if (swapcache)
+ return swapcache;
+
+ /*
+ * We might race against __swap_cache_del_folio(), and
+ * stumble across a swap_map entry whose SWAP_HAS_CACHE
+ * has not yet been cleared. Or race against another
+ * swap_cache_alloc_folio(), which has set SWAP_HAS_CACHE
+ * in swap_map, but not yet added its folio to swap cache.
+ */
+ schedule_timeout_uninterruptible(1);
+ }
+
+ __folio_set_locked(folio);
+ __folio_set_swapbacked(folio);
+
+ if (!charged && mem_cgroup_swapin_charge_folio(folio, NULL, gfp, entry)) {
+ put_swap_folio(folio, entry);
+ folio_unlock(folio);
+ return NULL;
+ }
+
+ swap_cache_add_folio(folio, entry, &shadow);
+ memcg1_swapin(entry, folio_nr_pages(folio));
+ if (shadow)
+ workingset_refault(folio, shadow);
+
+ /* Caller will initiate read into locked folio */
+ folio_add_lru(folio);
+ return folio;
+}
+
/**
* swap_cache_alloc_folio - Allocate folio for swapped out slot in swap cache.
* @entry: the swapped out swap entry to be binded to the folio.
{
struct swap_info_struct *si = __swap_entry_to_info(entry);
struct folio *folio;
- struct folio *new_folio = NULL;
struct folio *result = NULL;
- void *shadow = NULL;
*new_page_allocated = false;
- for (;;) {
- int err;
-
- /*
- * Check the swap cache first, if a cached folio is found,
- * return it unlocked. The caller will lock and check it.
- */
- folio = swap_cache_get_folio(entry);
- if (folio)
- goto got_folio;
-
- /*
- * Just skip read ahead for unused swap slot.
- */
- if (!swap_entry_swapped(si, entry))
- goto put_and_return;
-
- /*
- * Get a new folio to read into from swap. Allocate it now if
- * new_folio not exist, before marking swap_map SWAP_HAS_CACHE,
- * when -EEXIST will cause any racers to loop around until we
- * add it to cache.
- */
- if (!new_folio) {
- new_folio = folio_alloc_mpol(gfp_mask, 0, mpol, ilx, numa_node_id());
- if (!new_folio)
- goto put_and_return;
- }
-
- /*
- * Swap entry may have been freed since our caller observed it.
- */
- err = swapcache_prepare(entry, 1);
- if (!err)
- break;
- else if (err != -EEXIST)
- goto put_and_return;
-
- /*
- * Protect against a recursive call to swap_cache_alloc_folio()
- * on the same entry waiting forever here because SWAP_HAS_CACHE
- * is set but the folio is not the swap cache yet. This can
- * happen today if mem_cgroup_swapin_charge_folio() below
- * triggers reclaim through zswap, which may call
- * swap_cache_alloc_folio() in the writeback path.
- */
- if (skip_if_exists)
- goto put_and_return;
+ /* Check the swap cache again for readahead path. */
+ folio = swap_cache_get_folio(entry);
+ if (folio)
+ return folio;
- /*
- * We might race against __swap_cache_del_folio(), and
- * stumble across a swap_map entry whose SWAP_HAS_CACHE
- * has not yet been cleared. Or race against another
- * swap_cache_alloc_folio(), which has set SWAP_HAS_CACHE
- * in swap_map, but not yet added its folio to swap cache.
- */
- schedule_timeout_uninterruptible(1);
- }
-
- /*
- * The swap entry is ours to swap in. Prepare the new folio.
- */
- __folio_set_locked(new_folio);
- __folio_set_swapbacked(new_folio);
-
- if (mem_cgroup_swapin_charge_folio(new_folio, NULL, gfp_mask, entry))
- goto fail_unlock;
-
- swap_cache_add_folio(new_folio, entry, &shadow);
- memcg1_swapin(entry, 1);
+ /* Skip allocation for unused swap slot for readahead path. */
+ if (!swap_entry_swapped(si, entry))
+ return NULL;
- if (shadow)
- workingset_refault(new_folio, shadow);
-
- /* Caller will initiate read into locked new_folio */
- folio_add_lru(new_folio);
- *new_page_allocated = true;
- folio = new_folio;
-got_folio:
- result = folio;
- goto put_and_return;
-
-fail_unlock:
- put_swap_folio(new_folio, entry);
- folio_unlock(new_folio);
-put_and_return:
- if (!(*new_page_allocated) && new_folio)
- folio_put(new_folio);
+ /* Allocate a new folio to be added into the swap cache. */
+ folio = folio_alloc_mpol(gfp_mask, 0, mpol, ilx, numa_node_id());
+ if (!folio)
+ return NULL;
+ /* Try add the new folio, returns existing folio or NULL on failure. */
+ result = __swap_cache_prepare_and_add(entry, folio, gfp_mask,
+ false, skip_if_exists);
+ if (result == folio)
+ *new_page_allocated = true;
+ else
+ folio_put(folio);
return result;
}