From: Shivam Kalra <[email protected]>

A shmem folio is removed from its page cache when it is written to swap.
While it remains in the swap cache, it has no address_space mapping, so
folio_check_splittable() mistakes it for a truncated folio and rejects the
split with -EBUSY.

Allow a mappingless folio when it is in the swap cache. Initialize the
XArray state without an address-space mapping, then assign the XArray only
for mapped file folios. The split path already replaces each split folio
in the swap cache while holding the swap-cluster lock.

Signed-off-by: Shivam Kalra <[email protected]>
---
 mm/huge_memory.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 804b8f6aa557..ecfe40b1400b 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -3882,12 +3882,10 @@ int folio_check_splittable(struct folio *folio, 
unsigned int new_order,
        VM_WARN_ON_FOLIO(!folio_test_locked(folio), folio);
        /*
         * Folios that just got truncated cannot get split. Signal to the
-        * caller that there was a race.
-        *
-        * TODO: this will also currently refuse folios without a mapping in the
-        * swapcache (shmem or to-be-anon folios).
+        * caller that there was a race. A mappingless swapcache folio can be
+        * either shmem or not yet associated with an anon_vma, and is valid.
         */
-       if (!folio->mapping && !folio_test_anon(folio))
+       if (!folio->mapping && !folio_test_swapcache(folio))
                return -EBUSY;
 
        /* order-1 is not supported for anonymous THP. */
@@ -4022,10 +4020,7 @@ static int __folio_freeze_and_split_unmapped(struct 
folio *folio, unsigned int n
                        if (do_lru)
                                lru_add_split_folio(folio, new_folio, lruvec, 
list);
 
-                       /*
-                        * Anonymous folio with swap cache.
-                        * NOTE: shmem in swap cache is not supported yet.
-                        */
+                       /* Folio in the swap cache. */
                        if (ci) {
                                __swap_cache_replace_folio(ci, folio, 
new_folio);
                                continue;
@@ -4103,7 +4098,7 @@ static int __folio_split(struct folio *folio, unsigned 
int new_order,
                struct page *split_at, struct page *lock_at,
                struct list_head *list, enum split_type split_type)
 {
-       XA_STATE(xas, &folio->mapping->i_pages, folio->index);
+       XA_STATE(xas, NULL, folio->index);
        struct folio *end_folio = folio_next(folio);
        bool is_anon = folio_test_anon(folio);
        struct mem_cgroup *memcg, *old_memcg;
@@ -4158,11 +4153,12 @@ static int __folio_split(struct folio *folio, unsigned 
int new_order,
                }
                anon_vma_lock_write(anon_vma);
                mapping = NULL;
-       } else {
+       } else if (folio->mapping) {
                unsigned int min_order;
                gfp_t gfp;
 
                mapping = folio->mapping;
+               xas.xa = &mapping->i_pages;
                min_order = mapping_min_folio_order(mapping);
                if (new_order < min_order) {
                        ret = -EINVAL;

-- 
2.43.0



Reply via email to