VM_BUG_ON_PAGE(PageTail(page), page);
                array[i++] = page;
                rac->_batch_count += thp_nr_pages(page);
-
-               /*
-                * The page cache isn't using multi-index entries yet,
-                * so the xas cursor needs to be manually moved to the
-                * next index.  This can be removed once the page cache
-                * is converted.
-                */
-               if (PageHead(page))
-                       xas_set(&xas, rac->_index + rac->_batch_count);
-
                if (i == array_sz)
                        break;
        }
 
        }
 
        VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
-       VM_BUG_ON_FOLIO(nr != 1 && shadow, folio);
 
        xas_store(&xas, shadow);
        xas_init_marks(&xas);
                             struct folio_batch *fbatch)
 {
        XA_STATE(xas, &mapping->i_pages, fbatch->folios[0]->index);
-       int total_pages = 0;
+       long total_pages = 0;
        int i = 0;
        struct folio *folio;
 
 
                WARN_ON_ONCE(!folio_test_locked(folio));
 
-               if (folio->index == xas.xa_index)
-                       folio->mapping = NULL;
+               folio->mapping = NULL;
                /* Leave folio->index set: truncation lookup relies on it */
 
-               /*
-                * Move to the next folio in the batch if this is a regular
-                * folio or the index is of the last sub-page of this folio.
-                */
-               if (folio->index + folio_nr_pages(folio) - 1 == xas.xa_index)
-                       i++;
+               i++;
                xas_store(&xas, NULL);
-               total_pages++;
+               total_pages += folio_nr_pages(folio);
        }
        mapping->nrpages -= total_pages;
 }
                indices[fbatch->nr] = xas.xa_index;
                if (!folio_batch_add(fbatch, folio))
                        break;
-               goto next;
+               continue;
 unlock:
                folio_unlock(folio);
 put:
                folio_put(folio);
-next:
-               if (!xa_is_value(folio) && folio_test_large(folio)) {
-                       xas_set(&xas, folio->index + folio_nr_pages(folio));
-                       /* Did we wrap on 32-bit? */
-                       if (!xas.xa_index)
-                               break;
-               }
        }
        rcu_read_unlock();
 
        return folio_batch_count(fbatch);
 }
 
+static inline
+bool folio_more_pages(struct folio *folio, pgoff_t index, pgoff_t max)
+{
+       if (!folio_test_large(folio) || folio_test_hugetlb(folio))
+               return false;
+       if (index >= max)
+               return false;
+       return index < folio->index + folio_nr_pages(folio) - 1;
+}
+
 /**
  * find_get_pages_range - gang pagecache lookup
  * @mapping:   The address_space to search
                if (xa_is_value(folio))
                        continue;
 
+again:
                pages[ret] = folio_file_page(folio, xas.xa_index);
                if (++ret == nr_pages) {
                        *start = xas.xa_index + 1;
                        goto out;
                }
+               if (folio_more_pages(folio, xas.xa_index, end)) {
+                       xas.xa_index++;
+                       folio_ref_inc(folio);
+                       goto again;
+               }
        }
 
        /*
                if (unlikely(folio != xas_reload(&xas)))
                        goto put_page;
 
-               pages[ret] = &folio->page;
+again:
+               pages[ret] = folio_file_page(folio, xas.xa_index);
                if (++ret == nr_pages)
                        break;
+               if (folio_more_pages(folio, xas.xa_index, ULONG_MAX)) {
+                       xas.xa_index++;
+                       folio_ref_inc(folio);
+                       goto again;
+               }
                continue;
 put_page:
                folio_put(folio);
                        break;
                if (folio_test_readahead(folio))
                        break;
-               xas.xa_index = folio->index + folio_nr_pages(folio) - 1;
-               xas.xa_offset = (xas.xa_index >> xas.xa_shift) & XA_CHUNK_MASK;
+               xas_advance(&xas, folio->index + folio_nr_pages(folio) - 1);
                continue;
 put_folio:
                folio_put(folio);
        addr = vma->vm_start + ((start_pgoff - vma->vm_pgoff) << PAGE_SHIFT);
        vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd, addr, &vmf->ptl);
        do {
+again:
                page = folio_file_page(folio, xas.xa_index);
                if (PageHWPoison(page))
                        goto unlock;
                do_set_pte(vmf, page, addr);
                /* no need to invalidate: a not-present page won't be cached */
                update_mmu_cache(vma, addr, vmf->pte);
+               if (folio_more_pages(folio, xas.xa_index, end_pgoff)) {
+                       xas.xa_index++;
+                       folio_ref_inc(folio);
+                       goto again;
+               }
                folio_unlock(folio);
                continue;
 unlock:
+               if (folio_more_pages(folio, xas.xa_index, end_pgoff)) {
+                       xas.xa_index++;
+                       goto again;
+               }
                folio_unlock(folio);
                folio_put(folio);
        } while ((folio = next_map_page(mapping, &xas, end_pgoff)) != NULL);
 
 {
        struct page *head = compound_head(page);
        struct deferred_split *ds_queue = get_deferred_split_queue(head);
+       XA_STATE(xas, &head->mapping->i_pages, head->index);
        struct anon_vma *anon_vma = NULL;
        struct address_space *mapping = NULL;
        int extra_pins, ret;
                        goto out;
                }
 
+               xas_split_alloc(&xas, head, compound_order(head),
+                               mapping_gfp_mask(mapping) & GFP_RECLAIM_MASK);
+               if (xas_error(&xas)) {
+                       ret = xas_error(&xas);
+                       goto out;
+               }
+
                anon_vma = NULL;
                i_mmap_lock_read(mapping);
 
        /* block interrupt reentry in xa_lock and spinlock */
        local_irq_disable();
        if (mapping) {
-               XA_STATE(xas, &mapping->i_pages, page_index(head));
-
                /*
                 * Check if the head page is present in page cache.
                 * We assume all tail are present too, if head is there.
                 */
-               xa_lock(&mapping->i_pages);
+               xas_lock(&xas);
+               xas_reset(&xas);
                if (xas_load(&xas) != head)
                        goto fail;
        }
                if (mapping) {
                        int nr = thp_nr_pages(head);
 
+                       xas_split(&xas, head, thp_order(head));
                        if (PageSwapBacked(head)) {
                                __mod_lruvec_page_state(head, NR_SHMEM_THPS,
                                                        -nr);
                spin_unlock(&ds_queue->split_queue_lock);
 fail:
                if (mapping)
-                       xa_unlock(&mapping->i_pages);
+                       xas_unlock(&xas);
                local_irq_enable();
                remap_page(head, thp_nr_pages(head));
                ret = -EBUSY;
        if (mapping)
                i_mmap_unlock_read(mapping);
 out:
+       /* Free any memory we didn't use */
+       xas_nomem(&xas, 0);
        count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
        return ret;
 }
 
        }
        count_memcg_page_event(new_page, THP_COLLAPSE_ALLOC);
 
-       /* This will be less messy when we use multi-index entries */
+       /*
+        * Ensure we have slots for all the pages in the range.  This is
+        * almost certainly a no-op because most of the pages must be present
+        */
        do {
                xas_lock_irq(&xas);
                xas_create_range(&xas);
                        __mod_lruvec_page_state(new_page, NR_SHMEM, nr_none);
        }
 
+       /* Join all the small entries into a single multi-index entry */
+       xas_set_order(&xas, start, HPAGE_PMD_ORDER);
+       xas_store(&xas, new_page);
 xa_locked:
        xas_unlock_irq(&xas);
 xa_unlocked:
                        continue;
                }
 
+               /*
+                * XXX: khugepaged should compact smaller compound pages
+                * into a PMD sized page
+                */
                if (PageTransCompound(page)) {
                        result = SCAN_PAGE_COMPOUND;
                        break;
 
        }
 
        xas_store(&xas, newfolio);
-       if (nr > 1) {
-               int i;
-
-               for (i = 1; i < nr; i++) {
-                       xas_next(&xas);
-                       xas_store(&xas, newfolio);
-               }
-       }
 
        /*
         * Drop cache reference from old page by unfreezing
 
                                   struct mm_struct *charge_mm)
 {
        XA_STATE_ORDER(xas, &mapping->i_pages, index, compound_order(page));
-       unsigned long i = 0;
        unsigned long nr = compound_nr(page);
        int error;
 
        cgroup_throttle_swaprate(page, gfp);
 
        do {
-               void *entry;
                xas_lock_irq(&xas);
-               entry = xas_find_conflict(&xas);
-               if (entry != expected)
+               if (expected != xas_find_conflict(&xas)) {
+                       xas_set_err(&xas, -EEXIST);
+                       goto unlock;
+               }
+               if (expected && xas_find_conflict(&xas)) {
                        xas_set_err(&xas, -EEXIST);
-               xas_create_range(&xas);
-               if (xas_error(&xas))
                        goto unlock;
-next:
-               xas_store(&xas, page);
-               if (++i < nr) {
-                       xas_next(&xas);
-                       goto next;
                }
+               xas_store(&xas, page);
+               if (xas_error(&xas))
+                       goto unlock;
                if (PageTransHuge(page)) {
                        count_vm_event(THP_FILE_ALLOC);
                        __mod_lruvec_page_state(page, NR_SHMEM_THPS, nr);