Swap a large anon folio back in as a unit when its vswap entries share a contiguous run of physical swap slots on a synchronous IO device, instead of always falling back to order-0 faults. A zswap-backed or mixed-backing batch is still refused, and the fault retries at a smaller order. Signed-off-by: Nhat Pham --- mm/memory.c | 5 +++-- mm/swap_state.c | 17 +++++++++++++---- mm/zswap.c | 19 +++++++++++++------ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index dc4dd72ce73b..62f7b82427e2 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4823,9 +4823,10 @@ static unsigned long thp_swapin_suitable_orders(struct vm_fault *vmf) * lack handling for such cases, so fallback to swapping in order-0 * folio. * - * THP swapin for vswap is not supported yet either. + * Vswap entries are checked later, under the cluster lock in + * __swap_cache_add_check(). */ - if (is_vswap_entry(entry) || !zswap_never_enabled()) + if (!is_vswap_entry(entry) && !zswap_never_enabled()) return 0; /* diff --git a/mm/swap_state.c b/mm/swap_state.c index c0441783b8e7..5cfddec8633b 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -174,6 +174,9 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci, unsigned int ci_off, ci_end; unsigned long old_tb; bool is_zero; + struct swap_cluster_info_dynamic *ci_dyn; + enum vswap_backing_type type; + int ret; lockdep_assert_held(&ci->lock); @@ -202,11 +205,17 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci, return 0; /* - * Reject a vswap batch so swap_cache_alloc_folio falls back to - * order 0. + * For a vswap entry batch, reject if the backing is not THP-amenable + * (e.g. uniformly ZSWAP, or mixed). The order-fallback loop in + * swap_cache_alloc_folio will retry with a smaller order on -EBUSY. */ - if (is_vswap_entry(targ_entry)) - return -EBUSY; + if (is_vswap_entry(targ_entry)) { + ci_dyn = container_of(ci, struct swap_cluster_info_dynamic, ci); + ret = __vswap_check_backing(ci_dyn, round_down(ci_off, nr), + nr, &type); + if (ret != nr || type == VSWAP_ZSWAP) + return -EBUSY; + } is_zero = __swap_table_test_zero(ci, ci_off); ci_off = round_down(ci_off, nr); diff --git a/mm/zswap.c b/mm/zswap.c index 9a00ee049cf4..f16c0b44b5d5 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -1629,9 +1629,9 @@ bool zswap_store(struct folio *folio) * will SIGBUS). * * -EINVAL: if the swapped out content was in zswap, but the page belongs - * to a large folio, which is not supported by zswap. The folio is unlocked, - * but NOT marked up-to-date, so that an IO error is emitted (e.g. - * do_swap_page() will SIGBUS). + * to a large non-vswap folio, which is not supported by zswap. The folio + * is unlocked, but NOT marked up-to-date, so that an IO error is emitted + * (e.g. do_swap_page() will SIGBUS). * * -ENOENT: if the swapped out content was not in zswap. The folio remains * locked on return. @@ -1652,10 +1652,17 @@ int zswap_load(struct folio *folio) * Large folios should not be swapped in while zswap is being used, as * they are not properly handled. Zswap does not properly load large * folios, and a large folio may only be partially in zswap. + * + * A large vswap folio cannot reach here ZSWAP-backed, since + * __swap_cache_add_check() refuses such a batch, so hand it to the + * phys path without warning. */ - if (WARN_ON_ONCE(folio_test_large(folio))) { - folio_unlock(folio); - return -EINVAL; + if (folio_test_large(folio)) { + if (WARN_ON_ONCE(!swap_is_vswap(si))) { + folio_unlock(folio); + return -EINVAL; + } + return -ENOENT; } entry = zswap_entry_load(swp); -- 2.53.0-Meta