* [RFC] mm: Is flush_cache_page() in wp_page_reuse() really necessary?
[not found] <7a217590-1e37-4d80-9c5c-1f7d2c2b556c@huawei.com>
@ 2025-09-29 7:54 ` wuyifeng (C)
[not found] ` <f775b4a3-77c4-46a8-92e2-17308097df3e@redhat.com>
1 sibling, 0 replies; 2+ messages in thread
From: wuyifeng (C) @ 2025-09-29 7:54 UTC (permalink / raw)
To: linux-mm, akpm
[Forwarded] This is the email I previously sent to the maintainer.
I forgot to copy the mailing list, so I am resending it here.
-------- 转发的消息 --------
主题: [RFC] mm: Is flush_cache_page() in wp_page_reuse() really necessary?
日期: Mon, 29 Sep 2025 15:29:37 +0800
From: wuyifeng (C) <wuyifeng10@huawei.com>
收件人: David Hildenbrand <david@redhat.com>, akpm@linux-foundation.org, lorenzo.stoakes@oracle.com
抄送: baohua@kernel.org
Hi Linux MM developers,
I am reviewing the page fault handling code in the memory management
subsystem and came across a point that puzzled me regarding the use of
flush_cache_page().
When I referred to Documentation/core-api/cachetlb.rst, it describes a
typical sequence in a page fault scenario:
flush_cache_page(vma, addr, pfn);
set_pte(pte_pointer, new_pte_val);
flush_tlb_page(vma, addr);
That is, first the CPU cache is flushed, then the PTE is updated, and
finally the TLB is flushed for the corresponding virtual address. This
makes sense: when the virtual-to-physical mapping changes, flushing the
CPU cache is necessary to prevent stale or corrupted cache from affecting
the new mapping.
However, in wp_page_reuse(), the virtual-to-physical mapping does
not change. The function looks like this:
static inline void wp_page_reuse(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct page *page = vmf->page;
pte_t entry;
if (page)
page_cpupid_xchg_last(page, (1 << LAST_CPUPID_SHIFT) - 1);
flush_cache_page(vma, vmf->address, pte_pfn(vmf->orig_pte));
entry = pte_mkyoung(vmf->orig_pte);
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
if (ptep_set_access_flags(vma, vmf->address, vmf->pte, entry, 1))
update_mmu_cache(vma, vmf->address, vmf->pte);
}
Since the mapping itself does not change, I am puzzled whether this
flush_cache_page() call is actually necessary.
A similar situation appears in do_huge_pmd_wp_page(), which handles
huge pages. When the system reaches the reuse branch after the relevant
checks, the code looks like this:
reuse:
entry = pmd_mkyoung(orig_pmd);
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
if (pmdp_set_access_flags(vma, haddr, vmf->pmd, entry, 1))
update_mmu_cache_pmd(vma, vmf->address, vmf->pmd);
Here, flush_cache_page() is not called. Instead, the code relies on
updating PMD access flags and calling update_mmu_cache_pmd() to refresh
the TLB.
I have traced this back to the 2.6 kernel, and observed that the
`flush_cache_page` interface has already been present in the
reuse-related logic since then.
It appears that this part has not
received much attention over time, and the invocation of
`flush_cache_page` here may indeed be redundant?
code in do_wp_page (linux 2.6):
/* reuse the old page */
if (PageAnon(old_page) && !TestSetPageLocked(old_page)) {
int reuse = can_share_swap_page(old_page);
unlock_page(old_page);
if (reuse) {
flush_cache_page(vma, address, pfn);
entry = maybe_mkwrite(pte_mkyoung(pte_mkdirty(pte)),
vma);
ptep_set_access_flags(vma, address, page_table, entry, 1);
update_mmu_cache(vma, address, entry);
lazy_mmu_prot_update(entry);
pte_unmap(page_table);
spin_unlock(&mm->page_table_lock);
return VM_FAULT_MINOR|VM_FAULT_WRITE;
}
}
So my questions are:
Is the flush_cache_page() call in wp_page_reuse() actually
necessary?
If it is necessary, why is flush_cache_page() not required in the
reuse branch of do_huge_pmd_wp_page() for huge pages?
I would greatly appreciate your reply. Thank you very much!
Best regards,
[wuyifeng]
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [RFC] mm: Is flush_cache_page() in wp_page_reuse() really necessary?
[not found] ` <f775b4a3-77c4-46a8-92e2-17308097df3e@redhat.com>
@ 2025-09-29 8:52 ` wuyifeng (C)
0 siblings, 0 replies; 2+ messages in thread
From: wuyifeng (C) @ 2025-09-29 8:52 UTC (permalink / raw)
To: David Hildenbrand, akpm, lorenzo.stoakes; +Cc: baohua, linux-mm
Thanks for your reply and the pointers. I went through the kernel arch/ code
and looked into architectures that support PMD-level THP, such as arm64 and
x86_64 etc. I found that their flush_cache_page() implementations are all nop.
More precisely, for modern cache architectures (VIPT no-alias, PIPT),
flush_cache_page() is nop.
So the call in wp_page_reuse() looks more like a historical artifact, and it’s
hard to judge whether it was strictly necessary.
I really appreciate your clarifications!
Best regards,
Wuyifeng
在 2025/9/29 16:09, David Hildenbrand 写道:
> On 29.09.25 09:29, wuyifeng (C) wrote:
>> Hi Linux MM developers,
>
> Hi,
>
>>
>> I am reviewing the page fault handling code in the memory management
>> subsystem and came across a point that puzzled me regarding the use of
>> flush_cache_page().
>>
>> When I referred to Documentation/core-api/cachetlb.rst, it describes a
>> typical sequence in a page fault scenario:
>>
>> flush_cache_page(vma, addr, pfn);
>> set_pte(pte_pointer, new_pte_val);
>> flush_tlb_page(vma, addr);
>>
>
> The number of archs that actually implement flush_cache_page() is limited, so likely there is not a lot of interest around optimizing some out.
>
>>
>> That is, first the CPU cache is flushed, then the PTE is updated, and
>> finally the TLB is flushed for the corresponding virtual address. This
>> makes sense: when the virtual-to-physical mapping changes, flushing the
>> CPU cache is necessary to prevent stale or corrupted cache from affecting
>> the new mapping.
>>
>> However, in wp_page_reuse(), the virtual-to-physical mapping does
>> not change. The function looks like this:
>>
>> static inline void wp_page_reuse(struct vm_fault *vmf)
>> {
>> struct vm_area_struct *vma = vmf->vma;
>> struct page *page = vmf->page;
>> pte_t entry;
>>
>> if (page)
>> page_cpupid_xchg_last(page, (1 << LAST_CPUPID_SHIFT) - 1);
>>
>> flush_cache_page(vma, vmf->address, pte_pfn(vmf->orig_pte));
>> entry = pte_mkyoung(vmf->orig_pte);
>> entry = maybe_mkwrite(pte_mkdirty(entry), vma);
>> if (ptep_set_access_flags(vma, vmf->address, vmf->pte, entry, 1))
>> update_mmu_cache(vma, vmf->address, vmf->pte);
>> }
>
> Note that an "old" PTE might, depending on the arch, not actually have the hw-present bit set, so next access would trigger a page fault to maintain the clean/old PTE bit in software.
>
> So it could easily be that we are transitioning from non-hw-present to hw-present here.
>
> If that requires a flush_cache_page(), I really don't know :)
>
>>
>>
>> Since the mapping itself does not change, I am puzzled whether this
>> flush_cache_page() call is actually necessary.
>
> Same here. I have no idea if the scenario described above would require it, or only when we are changing something present to something non-present or differently-present.
>
>>
>> A similar situation appears in do_huge_pmd_wp_page(), which handles
>> huge pages. When the system reaches the reuse branch after the relevant
>> checks, the code looks like this:
>>
>> reuse:
>> entry = pmd_mkyoung(orig_pmd);
>> entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
>> if (pmdp_set_access_flags(vma, haddr, vmf->pmd, entry, 1))
>> update_mmu_cache_pmd(vma, vmf->address, vmf->pmd);
>>
>>
>> Here, flush_cache_page() is not called. Instead, the code relies on
>> updating PMD access flags and calling update_mmu_cache_pmd() to refresh
>> the TLB.
>
> Does any architecture that cares about flush_cache_page() actually support PMD THPs?
>
> I mean, looking at mm/khugepaged.c, I cannot spot any flush_cache_*(). Maybe it's implicit, but my best guess is that no architecture that supports THPs really requires it?
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-09-29 8:52 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <7a217590-1e37-4d80-9c5c-1f7d2c2b556c@huawei.com>
2025-09-29 7:54 ` [RFC] mm: Is flush_cache_page() in wp_page_reuse() really necessary? wuyifeng (C)
[not found] ` <f775b4a3-77c4-46a8-92e2-17308097df3e@redhat.com>
2025-09-29 8:52 ` wuyifeng (C)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox