Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Dev Jain <dev.jain@arm.com>
To: Barry Song <baohua@kernel.org>
Cc: akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org,
	hughd@google.com, chrisl@kernel.org, kasong@tencent.com,
	riel@surriel.com, liam@infradead.org, vbabka@kernel.org,
	harry@kernel.org, jannh@google.com, lance.yang@linux.dev,
	baolin.wang@linux.alibaba.com, shikemeng@huaweicloud.com,
	nphamcs@gmail.com, baoquan.he@linux.dev, youngjun.park@lge.com,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	rppt@kernel.org, surenb@google.com, mhocko@suse.com,
	pfalcato@suse.de, ryan.roberts@arm.com,
	anshuman.khandual@arm.com, davem@davemloft.net,
	andreas@gaisler.com
Subject: Re: [PATCH v2 8/8] mm/rmap: batch unmap anonymous swap-backed large folios
Date: Thu, 10 Sep 2026 10:09:42 +0530	[thread overview]
Message-ID: <3f1798bb-c6b9-4c1f-9a9a-fe312c1a0602@arm.com> (raw)
In-Reply-To: <CAGsJ_4yQhafd8zoESxFza3N8kxWxpOpa1Pv-Sf+N=mCPz5or=w@mail.gmail.com>

+ sparc guys for batching arch_unmap_one

On 09/09/26 3:18 am, Barry Song wrote:
> On Tue, Sep 1, 2026 at 1:45 PM Dev Jain <dev.jain@arm.com> wrote:
>>
>> Enable batch clearing of ptes, and batch swap setting of ptes for anon
>> swap-backed folio unmapping.
>>
>> Processing all ptes of a large folio in one go helps us batch across
>> atomics (add_mm_counter etc), barriers (in the function
>> __folio_try_share_anon_rmap), repeated calls to page_vma_mapped_walk(),
>> to name a few. In general, batching helps us to execute similar code
>> together, making the execution of the program more memory and
>> CPU friendly.
>>
>> On arm64-contpte, batching also helps us avoid redundant ptep_get() calls
>> and TLB flushes while breaking the contpte mapping.
>>
>> The handling of anon-exclusivity is very similar to commit cac1db8c3aad
>> ("mm: optimize mprotect() by PTE batching"). Since folio_unmap_pte_batch()
>> won't look at the bits of the underlying page, we need to process
>> sub-batches of ptes pointing to pages which are same w.r.t exclusivity,
>> and batch set only those ptes to swap ptes in one go.
>>
>> arch_unmap_one() is only defined for sparc64; I am not comfortable
>> regarding the nuances between retrieving the pfn from pte_pfn() or from
>> (paddr = pte_val(oldpte) & _PAGE_PADDR_4V).
>>
>> (And, pte_next_pfn() can't even be called from arch_unmap_one() because
>> that file does not include pgtable.h) So just disable the
>> "sparc64-anon-swapbacked" case for now.
>>
>> We need to take care of rmap accounting (folio_remove_rmap_ptes) and
>> reference accounting (folio_put_refs) when anon folio unmap succeeds.
>> In case we partially batch the large folio and fail, we need to correctly
>> do the accounting for pages which were successfully unmapped. So, put
>> this accounting code (which is finish_folio_unmap()) in
>> __ttu_anon_swapbacked_folio() itself, instead of doing some horrible
>> goto jumping at the callsite of ttu_anon_folio().
>>
>> Similarly, do the finish_folio_unmap() in ttu_anon_folio itself for
>> the non-swapbacked (lazyfree) case.
>>
>> If the batch length is less than the number of pages in the folio, then
>> we must skip over this batch.
>>
>> The page_vma_mapped_walk API ensures this - check_pte() will return true
>> only if any of [pvmw->pfn, pvmw->pfn + nr_pages) is mapped by the pte.
>> There is no pfn underlying a swap pte, so check_pte returns false and we
>> keep skipping until we hit a present pte, which is where we want to start
>> unmapping from next.
>>
>> Remove the label finish_unmap since no goto callers are left now.
>>
>> Signed-off-by: Dev Jain <dev.jain@arm.com>
>> ---
>>  mm/rmap.c | 110 ++++++++++++++++++++++++++++++++++++++++--------------
>>  1 file changed, 81 insertions(+), 29 deletions(-)
>>
>> diff --git a/mm/rmap.c b/mm/rmap.c
>> index 1b9f07d4d1be9..68e0201ffd003 100644
>> --- a/mm/rmap.c
>> +++ b/mm/rmap.c
>> @@ -1964,12 +1964,14 @@ static inline unsigned int folio_unmap_pte_batch(struct folio *folio,
>>         end_addr = pmd_addr_end(addr, vma->vm_end);
>>         max_nr = (end_addr - addr) >> PAGE_SHIFT;
>>
>> -       /* We only support lazyfree or file folios batching for now ... */
>> -       if (folio_test_anon(folio) && folio_test_swapbacked(folio))
>> +       if (pte_unused(pte))
>>                 return 1;
>>
>> -       if (pte_unused(pte))
>> +#ifdef __HAVE_ARCH_UNMAP_ONE
>> +       /* Add batching support to arch_unmap_one() to remove this */
> 
> I'd like to make this clearer. For example, could we say that
> sparc has `arch_unmap_one()`, which doesn't support batching?
> 
> BTW, it shouldn't be too hard to save `nr_pages` tags, looking at
> the code:
> 
> static inline int arch_unmap_one(struct mm_struct *mm,
>                                  struct vm_area_struct *vma,
>                                  unsigned long addr, pte_t oldpte)
> {
>         if (adi_state.enabled && (pte_val(oldpte) & _PAGE_MCD_4V))
>                 return adi_save_tags(mm, vma, addr, oldpte);
>         return 0;
> }
> Maybe the sparc folks can handle this.

I have mentioned in the patch description why I wasn't comfortable changing
this.

Perhaps the sparc guys can help me with the best way. Otherwise I'll try
harder in the next iteration to solve it myself : )
> 
>> +       if (folio_test_anon(folio) && folio_test_swapbacked(folio))
>>                 return 1;
>> +#endif
>>
>>         /*
>>          * If unmap fails, we need to restore the ptes. To avoid accidentally
>> @@ -2139,16 +2141,25 @@ static pte_t swp_pte_prepare(swp_entry_t entry, pte_t old_pte,
>>         return swp_pte;
>>  }
>>
>> -static bool ttu_anon_swapbacked_folio(struct vm_area_struct *vma,
>> +static void finish_folio_unmap(struct vm_area_struct *vma,
>> +               struct folio *folio, struct page *page, unsigned long nr_pages)
> 
> We are not necessarily finishing the whole folio here, right?
> The name is a bit misleading to me, as it sounds like we're finishing
> the whole folio.
> 
> Maybe `finish_folio_unmap_batch()`?

Yes makes sense, it finishes the batch rather than finishing the folio.


> 
>> +{
>> +       folio_remove_rmap_ptes(folio, page, nr_pages, vma);
>> +       if (vma->vm_flags & VM_LOCKED)
>> +               mlock_drain_local();
>> +       folio_put_refs(folio, nr_pages);
>> +}
>> +
>> +static bool __ttu_anon_swapbacked_folio(struct vm_area_struct *vma,
>>                 struct folio *folio, struct page *page, unsigned long address,
>> -               pte_t *ptep, pte_t pteval)
>> +               pte_t *ptep, pte_t pteval, unsigned long nr_pages,
>> +               bool anon_exclusive)
>>  {
>> -       const bool anon_exclusive = folio_test_anon(folio) &&
>> -                                   PageAnonExclusive(page);
>>         swp_entry_t entry = page_swap_entry(page);
>>         struct mm_struct *mm = vma->vm_mm;
>> +       pte_t swp_pte;
>>
>> -       if (folio_dup_swap_pages(folio, page, 1) < 0)
>> +       if (folio_dup_swap_pages(folio, page, nr_pages) < 0)
>>                 return false;
>>
>>         /*
>> @@ -2157,21 +2168,57 @@ static bool ttu_anon_swapbacked_folio(struct vm_area_struct *vma,
>>          * so we'll not check/care.
>>          */
>>         if (arch_unmap_one(mm, vma, address, pteval) < 0) {
>> -               folio_put_swap_pages(folio, page, 1);
>> +               VM_WARN_ON(nr_pages != 1);
>> +               folio_put_swap_pages(folio, page, nr_pages);
>>                 return false;
>>         }
>>
>>         /* See folio_try_share_anon_rmap(): clear PTE first. */
>> -       if (anon_exclusive && folio_try_share_anon_rmap_pte(folio, page)) {
>> -               folio_put_swap_pages(folio, page, 1);
>> +       if (anon_exclusive &&
>> +           folio_try_share_anon_rmap_ptes(folio, page, nr_pages)) {
>> +               folio_put_swap_pages(folio, page, nr_pages);
>>                 return false;
>>         }
>>
>>         mm_prepare_for_swap_entries(mm);
>> -       dec_mm_counter(mm, MM_ANONPAGES);
>> -       inc_mm_counter(mm, MM_SWAPENTS);
>> -       set_pte_at(mm, address, ptep,
>> -                  swp_pte_prepare(entry, pteval, anon_exclusive));
>> +       add_mm_counter(mm, MM_ANONPAGES, -nr_pages);
>> +       add_mm_counter(mm, MM_SWAPENTS, nr_pages);
>> +       swp_pte = swp_pte_prepare(entry, pteval, anon_exclusive);
>> +       set_softleaf_ptes(mm, address, ptep, swp_pte, nr_pages);
>> +       finish_folio_unmap(vma, folio, page, nr_pages);
>> +       return true;
>> +}
>> +
>> +static bool ttu_anon_swapbacked_folio(struct vm_area_struct *vma,
>> +               struct folio *folio, struct page *first_page,
>> +               unsigned long address, pte_t *ptep, pte_t pteval,
>> +               unsigned long nr_pages)
>> +{
>> +       unsigned long batch_idx = 0;
>> +
>> +       while (nr_pages) {
>> +               bool anon_exclusive = PageAnonExclusive(first_page + batch_idx);
>> +               unsigned long len = page_anon_exclusive_batch(batch_idx,
>> +                               nr_pages, first_page, anon_exclusive);
> 
> `len` is really a bad name, as `len` usually describes a size.
> Maybe `batch_pages`?

I disagree here : ) I don't think someone should mistake len with size.
len is ... "length". So in this case it is the length of pages in the
array, starting from batch_idx, upto nr_pages, which are all exclusive
or not. Also I would prefer short variable names.

> 
>> +
>> +               if (!__ttu_anon_swapbacked_folio(vma, folio,
>> +                               first_page + batch_idx, address, ptep, pteval,
>> +                               len, anon_exclusive)) {
>> +                       /* Restore the remaining PTEs that were cleared. */
>> +                       set_ptes(vma->vm_mm, address, ptep, pteval, nr_pages);
>> +                       return false;
>> +               }
>> +
>> +               nr_pages -= len;
>> +               if (!nr_pages)
>> +                       break;
>> +
>> +               pteval = pte_advance_pfn(pteval, len);
>> +               address += len * PAGE_SIZE;
>> +               batch_idx += len;
>> +               ptep += len;
>> +       }
>> +
>>         return true;
>>  }
> 
> Best Regards
> Barry



  reply	other threads:[~2026-09-10  4:39 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  5:43 [PATCH v2 0/8] Optimize anonymous swapbacked large folio unmapping Dev Jain
2026-09-01  5:43 ` [PATCH v2 1/8] mm/swapfile: add batched version of folio_dup_swap Dev Jain
2026-09-01  5:43 ` [PATCH v2 2/8] mm/swapfile: add batched version of folio_put_swap Dev Jain
2026-09-01  5:43 ` [PATCH v2 3/8] mm: move anon-exclusive batch helper to mm.h Dev Jain
2026-09-01  5:49   ` Barry Song
2026-09-01  6:24     ` Dev Jain
2026-09-02  6:29       ` Barry Song
2026-09-04  3:46   ` Dev Jain
2026-09-01  5:43 ` [PATCH v2 4/8] mm/rmap: Add batched version of folio_try_share_anon_rmap_pte Dev Jain
     [not found]   ` <CAGsJ_4zWPXoJ5kiLoVcq1YHv6WcmjPEssTUs4OT15iNRob3_rQ@mail.gmail.com>
2026-09-09  7:44     ` Dev Jain
2026-09-01  5:43 ` [PATCH v2 5/8] mm/internal: rename swap offset helpers to softleaf offset Dev Jain
2026-09-05 10:42   ` Barry Song
2026-09-05 10:49     ` Barry Song
2026-09-07  5:38     ` Dev Jain
2026-09-07 21:33       ` Barry Song
2026-09-08  5:38         ` Dev Jain
2026-09-08  8:41           ` Garg, Shivank
2026-09-01  5:43 ` [PATCH v2 6/8] mm/internal: add set_softleaf_ptes Dev Jain
2026-09-05 10:52   ` Barry Song
2026-09-01  5:43 ` [PATCH v2 7/8] mm/memory: use set_softleaf_ptes for uffd-wp markers Dev Jain
2026-09-05 10:53   ` Barry Song
2026-09-01  5:43 ` [PATCH v2 8/8] mm/rmap: batch unmap anonymous swap-backed large folios Dev Jain
2026-09-08 21:48   ` Barry Song
2026-09-10  4:39     ` Dev Jain [this message]
2026-09-10  4:58       ` Barry Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3f1798bb-c6b9-4c1f-9a9a-fe312c1a0602@arm.com \
    --to=dev.jain@arm.com \
    --cc=akpm@linux-foundation.org \
    --cc=andreas@gaisler.com \
    --cc=anshuman.khandual@arm.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=baoquan.he@linux.dev \
    --cc=chrisl@kernel.org \
    --cc=davem@davemloft.net \
    --cc=david@kernel.org \
    --cc=harry@kernel.org \
    --cc=hughd@google.com \
    --cc=jannh@google.com \
    --cc=kasong@tencent.com \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mhocko@suse.com \
    --cc=nphamcs@gmail.com \
    --cc=pfalcato@suse.de \
    --cc=riel@surriel.com \
    --cc=rppt@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=shikemeng@huaweicloud.com \
    --cc=surenb@google.com \
    --cc=vbabka@kernel.org \
    --cc=youngjun.park@lge.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox