From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Li Zhe <lizhe.67@bytedance.com>,
"Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Cc: "Jose A. Perez de Azpillaga" <azpijr@gmail.com>,
akpm@linux-foundation.org, muchun.song@linux.dev,
osalvador@suse.de, linux-mm@kvack.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH] mm/hugetlb: avoid recursive i_mmap_rwsem in PMD sharing
Date: Thu, 15 Oct 2026 21:33:22 +0200 [thread overview]
Message-ID: <33e26c40-45f4-44a4-bfea-1ae7ff74e4cf@kernel.org> (raw)
In-Reply-To: <98d4a305-51e4-4374-b09d-7d251dbf166d@bytedance.com>
On 10/15/26 04:40, Li Zhe wrote:
> On 10/14/26 6:52 PM, David Hildenbrand (Arm) wrote:
>> On 10/14/26 08:39, Li Zhe wrote:
>>> Hi David,
>>>
>>> I thought more about the case where the destination page table was
>>> preallocated as a shared PMD.
>>>
>>> I do not think the current move_huge_pte() handles that case very well. The
>>> reason is that it assumes the destination PTE is not part of a shared PMD
>>> page table: it clears the source PTE and then installs that PTE into the
>>> destination. That works when the destination PMD page table is not shared,
>>> because the source removal and destination install have a net-zero effect on
>>> rmap and hugetlb accounting.
>>>
>>> If the destination PTE belongs to a shared PMD, that assumption does not
>>> hold. In that case, I think we should leave the shared destination page
>>> table untouched and treat the operation as a source-side unmap. Otherwise
>>> move_huge_pte() either writes through a shared page table or skips the
>>> source-side unmap accounting. That can make the rmap, hugetlb count and
>>> folio release handling inconsistent with the actual PTE update.
>>>
>>> So I think the change should be roughly:
>> IIUC, what you are saying is:
>>
>> A) If the source table is shared, unshare it.
>>
>> b) If the destination table is shared, there is nothing to do.
>>
>> ?
>
> Yes, mostly.
>
> If the source PMD table is shared, huge_pmd_unshare() already drops that
> shared PMD table from the source side and we skip moving individual PTEs
> from that table.
>
> If the source PTE is not in a shared PMD table, but the destination PTE
> is in a shared PMD table, then I think we should not install the source
> PTE into the destination page table. The destination mapping is already
> represented by the shared PMD page table.
>
> However, move_huge_pte() still has to handle the source side as an
> unmap: clear the source PTE, record the TLB removal, and update the
> source-side hugetlb accounting/rmap state. Otherwise the source mapping
> is removed from the page table without the matching source-side
> accounting. I was able to reproduce this as:
>
> BUG: Bad page cache in process init
> page dumped because: still mapped when deleted
>
> when keeping the preallocation approach but dropping the special handling
> for a destination PMD-shared page table.
>
> So my current understanding is:
>
> 1. source PMD shared: unshare it and skip the PMD-sized range;
> 2. destination PMD shared: do not modify the destination PTE, but
> still clear and account the source PTE removal.
Yes, I think this makes a lot of sense. Given that this issue has been
in the works for more than one month without a fix I tried to see how to do
it cleaner than your version:
This is what I came up with quickly, using existing helpers by refactoring them.
Is this what you had in mind? I didn't actually test this yet. If it makes sense,
could you test it and possibly clean it up further?
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index fb51f7d02cebf..0c3aebea7a2f8 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -124,6 +124,9 @@ static int __huge_pmd_unshare(struct mmu_gather *tlb,
bool check_locks);
static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
unsigned long start, unsigned long end, bool take_locks);
+static void hugetlb_unmap_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma, unsigned long start, unsigned long end,
+ struct folio *folio, zap_flags_t zap_flags);
static struct resv_map *vma_resv_map(struct vm_area_struct *vma);
static inline bool subpool_is_free(struct hugepage_subpool *spool)
@@ -5172,6 +5175,8 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
for (; old_addr < old_end; old_addr += sz, new_addr += sz) {
const unsigned long offset_to_last_entry =
(old_addr | last_addr_mask) - old_addr;
+ const unsigned long offset_to_last_dst_entry =
+ (new_addr | last_addr_mask) - new_addr;
src_pte = hugetlb_walk(vma, old_addr, sz);
if (!src_pte) {
@@ -5188,6 +5193,23 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
continue;
}
+ /*
+ * Don't try to install a shared PMD table and move stuff into
+ * them. Instead, just zap the source PMDs and share the PMD
+ * table later lazily.
+ */
+ if (sz == PMD_SIZE && want_pmd_share(new_vma, new_addr)) {
+ const unsigned long unmap_size = min(old_end - old_addr,
+ offset_to_last_dst_entry + sz);
+
+ hugetlb_unmap_range(&tlb, vma, old_addr,
+ old_addr + unmap_size, NULL,
+ ZAP_FLAG_DROP_MARKER);
+ old_addr += unmap_size - sz;
+ new_addr += unmap_size - sz;
+ continue;
+ }
+
dst_pte = huge_pte_alloc(mm, new_vma, new_addr, sz);
if (!dst_pte)
break;
@@ -5207,9 +5229,9 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
return len + old_addr - old_end;
}
-void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
- unsigned long start, unsigned long end,
- struct folio *folio, zap_flags_t zap_flags)
+static void hugetlb_unmap_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma, unsigned long start, unsigned long end,
+ struct folio *folio, zap_flags_t zap_flags)
{
struct mm_struct *mm = vma->vm_mm;
const bool folio_provided = !!folio;
@@ -5227,13 +5249,6 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
BUG_ON(start & ~huge_page_mask(h));
BUG_ON(end & ~huge_page_mask(h));
- /*
- * This is a hugetlb vma, all the pte entries should point
- * to huge page.
- */
- tlb_change_page_size(tlb, sz);
- tlb_start_vma(tlb, vma);
-
last_addr_mask = hugetlb_mask_last_page(h);
address = start;
for (; address < end; address += sz) {
@@ -5362,6 +5377,15 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
if (folio_provided)
break;
}
+}
+
+void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end,
+ struct folio *folio, zap_flags_t zap_flags)
+{
+ tlb_change_page_size(tlb, huge_page_size(hstate_vma(vma)));
+ tlb_start_vma(tlb, vma);
+ hugetlb_unmap_range(tlb, vma, start, end, folio, zap_flags);
tlb_end_vma(tlb, vma);
huge_pmd_unshare_flush(tlb, vma);
--
2.43.0
--
Cheers,
David
next prev parent reply other threads:[~2026-10-15 19:35 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 6:43 [PATCH] mm/hugetlb: avoid recursive i_mmap_rwsem in PMD sharing Li Zhe
2026-09-30 9:31 ` Jose A. Perez de Azpillaga
2026-10-05 9:03 ` Li Zhe
2026-10-05 9:25 ` David Hildenbrand (Arm)
2026-10-09 11:52 ` Lorenzo Stoakes (ARM)
2026-10-09 18:05 ` David Hildenbrand (Arm)
2026-10-10 6:37 ` Li Zhe
2026-10-10 13:18 ` David Hildenbrand (Arm)
2026-10-14 6:39 ` Li Zhe
2026-10-14 10:52 ` David Hildenbrand (Arm)
2026-10-15 2:40 ` Li Zhe
2026-10-15 19:33 ` David Hildenbrand (Arm) [this message]
2026-09-30 10:03 ` David Hildenbrand (Arm)
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=33e26c40-45f4-44a4-bfea-1ae7ff74e4cf@kernel.org \
--to=david@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=azpijr@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=lizhe.67@bytedance.com \
--cc=ljs@kernel.org \
--cc=muchun.song@linux.dev \
--cc=osalvador@suse.de \
/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