.../testing/sysfs-kernel-mm-transparent-hugepage | 1 + Documentation/admin-guide/mm/transhuge.rst | 27 +++ include/linux/huge_mm.h | 45 ++++- include/linux/mm.h | 19 ++ include/uapi/asm-generic/mman-common.h | 9 + mm/huge_memory.c | 198 ++++++++++++++++++--- mm/khugepaged.c | 8 +- mm/madvise.c | 25 +++ 8 files changed, 308 insertions(+), 24 deletions(-)
Copy on write support for anonymous pmd level THP is simple right now:
firstly we'll check whether the folio can be exclusively used by the
faulting process, if we can (when the ref of the folio is only 1 after
trying to free swapcache or the page flag AnonExclusive is setup) we'll
directly use it with few further handling. If we cannot, then we'll
split the pmd into 512 4K ptes, and do copy on write only for the
specific 4K page that we faulted on.
This logic is truly memory efficient since for most workloads we don't
want to allocate 2M new memory simply on a small write. However, it also
makes the original 2M page for the process suddenly splitted on a
write which will generate some performance thrashing. For example, if
process A and process B share an anonymous 2M pmd, if process B chooses
to do a writing, then its page table mapping will be changed from 1
pmd entry into 512 4K pte entries at once, so the tlb benifit will
suddenly just "vanish" for process B, which sometimes may cause a
observable performance degeneration. After that, we can only wait for
khugepaged to do the collapse for this area and merge the pmd back, which
is not easy to happen.
In addition to the problem above, this logic can also generate some
deficiency for THP itself. Currently THP is just a "best-effort" choice
with no "certainty". THP is easily splitted into multiple small pages
on common calling path like reclaiming, COW. A transparent splitting
can cause throughput fluctuation for some workloads. For these workloads,
we may want to give THP some "certainty" just like hugetlbfs, The effect
we want is: after some customized setup, if only the system has usable
folio, and the virtual memory alignment permits (or we setup to), we can
make sure we always use THP for it, the system will never split it except
the user wants to do so.
This patchset is about both two things above, firstly we add pmd level
THP COW support by revising the code in do_huge_pmd_wp_page, we added
switch for it because different workloads may need different resources,
for which memory saving may matter more rather than the 2M tlb gain.
The switch is very similar to the "enable" and "shmem_enable" in sysfs
path of transparent_hugepage. THP COW is only enabled when THP itself
is enabled globally or by madvise. And also, we add basic THP setup
helpers and branch in madvise path and add the THP COW choice to it for a
more fine-grained setup. Now the helpers only supports copy on write
related, but in the future we may be able to add more types of THP
configurations into it like swapping.
Patch Details:
========
* Patch 1 adds the basic THP setup helpers and branch in madvise path.
Then we add THP COW parameter into it.
* Patch 2 adds the THP COW sysfs interface, the logic is very similar
to enable and shmem_enable of THP.
* Patch 3 adds the helpers that will be used in the actual COW path
to decide whether we choose to do pmd level THP COW.
* Patch 4 reconstructs map_anon_folio_pmd_nopf and map_anon_folio_pmd_pf
to make it capable of doing mapping for copied new folio when the
fault flag has FLAG_FAULT_UNSHARE.
* Patch 5 adds the actual support for pmd level THP COW, and uses all
the switches and helpers in the above 4 patches to do the strategy
control.
Thanks for reading. Comments and suggestions are very welcome!
Signed-off-by: Luka Bai <lukabai@tencent.com>
---
Luka Bai (5):
mm: add basic madvise helpers and branch for THP setup
mm: add pmd level THP COW parameter in sysfs
mm: add pmd level THP COW judgement helpers
mm: enable map_anon_folio_pmd_nopf to handle unshare
mm: support choosing to do THP COW for anonymous pmd entry.
.../testing/sysfs-kernel-mm-transparent-hugepage | 1 +
Documentation/admin-guide/mm/transhuge.rst | 27 +++
include/linux/huge_mm.h | 45 ++++-
include/linux/mm.h | 19 ++
include/uapi/asm-generic/mman-common.h | 9 +
mm/huge_memory.c | 198 ++++++++++++++++++---
mm/khugepaged.c | 8 +-
mm/madvise.c | 25 +++
8 files changed, 308 insertions(+), 24 deletions(-)
---
base-commit: 41cd9e3d23b8fd9e6c3c0311e9cb0304442c6141
change-id: 20260501-thp_cow-94873ed30793
Best regards,
--
Luka Bai <lukabai@tencent.com>
On 5/1/26 07:55, Luka Bai wrote: Hi, > Copy on write support for anonymous pmd level THP is simple right now: > firstly we'll check whether the folio can be exclusively used by the > faulting process, if we can (when the ref of the folio is only 1 after > trying to free swapcache or the page flag AnonExclusive is setup) we'll > directly use it with few further handling. If we cannot, then we'll > split the pmd into 512 4K ptes, and do copy on write only for the > specific 4K page that we faulted on. > > This logic is truly memory efficient since for most workloads we don't > want to allocate 2M new memory simply on a small write. However, it also > makes the original 2M page for the process suddenly splitted on a > write which will generate some performance thrashing. For example, if > process A and process B share an anonymous 2M pmd, if process B chooses > to do a writing, then its page table mapping will be changed from 1 > pmd entry into 512 4K pte entries at once, so the tlb benifit will > suddenly just "vanish" for process B, which sometimes may cause a > observable performance degeneration. After that, we can only wait for > khugepaged to do the collapse for this area and merge the pmd back, which > is not easy to happen. You probably know that, historically, we did exactly what you describe in this patch set. It was rather bad regarding memory waste and COW latency, so we switched to the current model. Note that there was a recent related discussion for executable, which was rejected: https://lore.kernel.org/r/20251226100337.4171191-1-zhangqilong3@huawei.com > > In addition to the problem above, this logic can also generate some > deficiency for THP itself. Currently THP is just a "best-effort" choice > with no "certainty". THP is easily splitted into multiple small pages > on common calling path like reclaiming, COW. A transparent splitting > can cause throughput fluctuation for some workloads. For these workloads, > we may want to give THP some "certainty" just like hugetlbfs, There are no such guarantees, though. And We wouldn't want to commit to any such guarantees today. For example, simple page migration can split the folio. Allocation failures will fallback to small pages etc. If you need guarantees, use hugetlb for now. > The effect > we want is: after some customized setup, if only the system has usable > folio, and the virtual memory alignment permits (or we setup to), we can > make sure we always use THP for it, the system will never split it except > the user wants to do so. > > This patchset is about both two things above, firstly we add pmd level > THP COW support by revising the code in do_huge_pmd_wp_page, we added > switch for it because different workloads may need different resources, The switch is bad, and we won't accept any toggle like that. A system-wide setting does not make sense for such behavior. A per-VMA flag? Maybe, but I expect pushback as well, as it is way too specific. So we'd have to find some concept that abstracts these semantics. But I expect pushback as well. We messed up enough with toggles in THP space, unfortunately. Also, anything that only works for PMD-sized THPs is a warning sign in 2026 :) You don't really raise any concrete use cases or performance numbers for these use cases. Some details about applications that use fork() and rely on such behavior would be helpful. Note that an application that does fork() could use MADV_COLLAPSE after fork() to make sure that it immediately gets THPs back. There is also the option to just use MADV_DONTFORK to not even share ranges with a child process in the first place, avoiding page copies entirely. -- Cheers, David
在 Fri, May 01, 2026 at 09:07:49AM +0200,David Hildenbrand (Arm) 写道: Hi David, Thanks for your review and opinion :) I really appreciate it! I'm sorry that I'm not that familiar with the mail sending in lore, so my earlier reply in https://lore.kernel.org/all/8F6F0691-91A1-4645-A218-2219DE6047AD@icloud.com/ may be in a wrong format, if you haven't read it, just ignore it, and read this mail instead, I also revised the reply a little in this mail. thanks. :) > On 5/1/26 07:55, Luka Bai wrote: > > Hi, > > > Copy on write support for anonymous pmd level THP is simple right now: > > firstly we'll check whether the folio can be exclusively used by the > > faulting process, if we can (when the ref of the folio is only 1 after > > trying to free swapcache or the page flag AnonExclusive is setup) we'll > > directly use it with few further handling. If we cannot, then we'll > > split the pmd into 512 4K ptes, and do copy on write only for the > > specific 4K page that we faulted on. > > > > This logic is truly memory efficient since for most workloads we don't > > want to allocate 2M new memory simply on a small write. However, it also > > makes the original 2M page for the process suddenly splitted on a > > write which will generate some performance thrashing. For example, if > > process A and process B share an anonymous 2M pmd, if process B chooses > > to do a writing, then its page table mapping will be changed from 1 > > pmd entry into 512 4K pte entries at once, so the tlb benifit will > > suddenly just "vanish" for process B, which sometimes may cause a > > observable performance degeneration. After that, we can only wait for > > khugepaged to do the collapse for this area and merge the pmd back, which > > is not easy to happen. > > You probably know that, historically, we did exactly what you describe in this > patch set. It was rather bad regarding memory waste and COW latency, so we > switched to the current model. > > Note that there was a recent related discussion for executable, which was rejected: > > https://lore.kernel.org/r/20251226100337.4171191-1-zhangqilong3@huawei.com > Yes, I know this history, and I know that it will cost some memory or latency, That’s why I was wondering maybe I can add a switch to it to make it configurable :). But I don’t know the existence of the discussion in https://lore.kernel.org/r/20251226100337.4171191-1-zhangqilong3@huawei.com, I’ll check it out, thanks for informing me that :). > > > > In addition to the problem above, this logic can also generate some > > deficiency for THP itself. Currently THP is just a "best-effort" choice > > with no "certainty". THP is easily splitted into multiple small pages > > on common calling path like reclaiming, COW. A transparent splitting > > can cause throughput fluctuation for some workloads. For these workloads, > > we may want to give THP some "certainty" just like hugetlbfs, > > There are no such guarantees, though. And We wouldn't want to commit to any such > guarantees today. For example, simple page migration can split the folio. > Allocation failures will fallback to small pages etc. > > If you need guarantees, use hugetlb for now. > The reason why I want to use THP over hugetlb is that I need reclamation for my workload :). There are many processes in my workload that need 2M aligned folios for better performance, and we want to reclaim them back automatically when the process doesn’t need the folios. But hugetlbfs cannot do passive reclamation from what I know (except doing active madvise by the processes themselves). And using THP can easily split the hugepages. So that’s why I would like to add certainty for THP, and use THP for these processes as backend, because THP is very well integrated with the swap system and other filesystems. And from what I checked, it seems the most common case for splitting a THP is COW and swapping so I am trying to handle these two scenarios (But coincidentally, PMD swapping is committed in https://lore.kernel.org/all/D3F08F85-76E0-4C5A-ABA1-537C68E038B8@nvidia.com/ a few days before, which is a great implementation :) ). > > The effect > > we want is: after some customized setup, if only the system has usable > > folio, and the virtual memory alignment permits (or we setup to), we can > > make sure we always use THP for it, the system will never split it except > > the user wants to do so. > > > > This patchset is about both two things above, firstly we add pmd level > > THP COW support by revising the code in do_huge_pmd_wp_page, we added > > switch for it because different workloads may need different resources, > > The switch is bad, and we won't accept any toggle like that. A system-wide > setting does not make sense for such behavior. > Oh, the reason why I added a switch globally is also because the scenario I mentioned above, I want those processes to always use PMD sized folios as backend to make sure performance. COW is truly not that common like swap out/swap in, it just can happen sometimes, which I guess the reason may be about image duplication. Setting the system globally is more convenient for my situation :). I can go without this global switch if it's more reasonable. > A per-VMA flag? Maybe, but I expect pushback as well, as it is way too specific. > So we'd have to find some concept that abstracts these semantics. But I expect > pushback as well. > > We messed up enough with toggles in THP space, unfortunately. > > Also, anything that only works for PMD-sized THPs is a warning sign in 2026 :) > And for PMD-sized THPs, actually, I’m also considering adding more support for COW to mTHP if the upstream consider it useful. And also for pud sized THPs also. But I guess I have to firstly handle stage 1: PMD level COW now :). And also, since PMD sized folio is commonly used in my workload, I'm also wondering digging into pmd sized KSM in the future, in which I think pmd sized COW may be more useful then :). > You don't really raise any concrete use cases or performance numbers for these > use cases. Some details about applications that use fork() and rely on such > behavior would be helpful. > Sorry for that, the concrete workload itself hasn't been finished yet. Now it just can happen sometimes in my multi-2M-sized-processes workload test. But the user of our 2M sized folio schema is actually not necessarily myself but also can be the userspace developers. I cannot guarantee that fork will not be used in the performance test of their workload since that is a normal posix call. Maybe a little overthinking? :) I just think swap and COW are two main scenarios that may transparently split pmd sized folios, so maybe we can solve it and make THP both reclaimable and stable. Maybe that can make THP more widely used in real deployed environment since the resource can become more controllable for the users :). That's why I was thinking maybe implementing it with setup switches is a reasonable solution? > Note that an application that does fork() could use MADV_COLLAPSE after fork() > to make sure that it immediately gets THPs back. > > There is also the option to just use MADV_DONTFORK to not even share ranges with > a child process in the first place, avoiding page copies entirely. > MADV_DONTFORK and MADV_COLLAPSE are nice and great options :), but the former one seems to be a little wasteful :). And the latter one can greatly solve fork situation, but we have to recognize all the possible regions that may be accessed in performance test and collapse them. And it's also not easy to solve the situation like pmd swap in for pmd pages mapped by more than two processes. :) > -- > Cheers, > > David Look forward to your further opionion, thanks! Best, Luka
>> >> Note that there was a recent related discussion for executable, which was rejected: >> >> https://lore.kernel.org/r/20251226100337.4171191-1-zhangqilong3@huawei.com >> > > Yes, I know this history, and I know that it will cost some memory or latency, > That’s why I was wondering maybe I can add a switch to it to make it > configurable :). Switches for something like that is just not a good fit. For example, for a short-lived child (e.g., fork+exec) it usually makes no sense to cow a larger chunk of address space, when you know that it will exit immediately either way and free up the memory. >>> >>> In addition to the problem above, this logic can also generate some >>> deficiency for THP itself. Currently THP is just a "best-effort" choice >>> with no "certainty". THP is easily splitted into multiple small pages >>> on common calling path like reclaiming, COW. A transparent splitting >>> can cause throughput fluctuation for some workloads. For these workloads, >>> we may want to give THP some "certainty" just like hugetlbfs, >> >> There are no such guarantees, though. And We wouldn't want to commit to any such >> guarantees today. For example, simple page migration can split the folio. >> Allocation failures will fallback to small pages etc. >> >> If you need guarantees, use hugetlb for now. >> > > The reason why I want to use THP over hugetlb is that I need reclamation for my > workload :). There are many processes in my workload that need 2M > aligned folios for better performance, and we want to reclaim them back automatically > when the process doesn’t need the folios. Can you share some details how exactly that is supposed to work? > But hugetlbfs cannot do passive reclamation > from what I know (except doing active madvise by the processes themselves). And using Right, you can only return hugetlb folios by doing MADV_DONTNEED or munmap(). > THP can easily split the hugepages. So that’s why I would like to add certainty for THP, Repeat after me: there are no guarantees. There is no certainty :) > and use THP for these processes as backend, because THP is very well integrated with > the swap system and other filesystems. And from what I checked, > it seems the most common case for splitting a THP is COW and swapping so I am trying > to handle these two scenarios (But coincidentally, PMD swapping is committed in > https://lore.kernel.org/all/D3F08F85-76E0-4C5A-ABA1-537C68E038B8@nvidia.com/ > a few days before, which is a great implementation :) ). Right, but that really only changes how we map large folios, not how we allocate them. There are no guarantees. > >>> The effect >>> we want is: after some customized setup, if only the system has usable >>> folio, and the virtual memory alignment permits (or we setup to), we can >>> make sure we always use THP for it, the system will never split it except >>> the user wants to do so. >>> >>> This patchset is about both two things above, firstly we add pmd level >>> THP COW support by revising the code in do_huge_pmd_wp_page, we added >>> switch for it because different workloads may need different resources, >> >> The switch is bad, and we won't accept any toggle like that. A system-wide >> setting does not make sense for such behavior. >> > > Oh, the reason why I added a switch globally is also because the scenario I mentioned > above, I want those processes to always use PMD sized folios as backend to make sure > performance. "Always" is wishful thinking in many scenarios I'm afraid. > COW is truly not that common like swap out/swap in, it just can happen > sometimes, which I guess the reason may be about image duplication. Setting the system > globally is more convenient for my situation :). I can go without this global switch > if it's more reasonable. > >> A per-VMA flag? Maybe, but I expect pushback as well, as it is way too specific. >> So we'd have to find some concept that abstracts these semantics. But I expect >> pushback as well. >> >> We messed up enough with toggles in THP space, unfortunately. >> >> Also, anything that only works for PMD-sized THPs is a warning sign in 2026 :) >> > > And for PMD-sized THPs, actually, I’m also considering adding more support for > COW to mTHP if the upstream consider it useful. And also for pud sized THPs > also. But I guess I have to firstly handle stage 1: PMD level COW now :). > And also, since PMD sized folio is commonly used in my workload, I'm also wondering > digging into pmd sized KSM in the future, in which I think pmd sized COW may > be more useful then :). I'm afraid I have to stop you right there: there has to be a pretty convincing story to add any of that. In particular KSM with large folios (/me shivering). But I already don't buy the COW story. Just configure khugepaged in a better way or use MADV_COLLAPSE and you don't really need to modify the kernel at all in 99.99% of the case. (khugepaged needs a lot of tuning work, it's currently not the smartest implementation) Maybe, we might give khugepaged better direction of what to try scanning next (e.g., where we just COW'ed a THP). Not sure, there are plenty of things to explore. > >> You don't really raise any concrete use cases or performance numbers for these >> use cases. Some details about applications that use fork() and rely on such >> behavior would be helpful. >> > > Sorry for that, the concrete workload itself hasn't been finished yet. Okay, what I thought after seeing no workloads an no performance numbers :) If you don't know the workload, how can you claim that the additional latency and/or memory consumption is not a problem? Also: there is no guarantee that you will actually succeed in allocating a PMD THP during a COW fault. > Now it just > can happen sometimes in my multi-2M-sized-processes workload test. But the user > of our 2M sized folio schema is actually not necessarily myself but also can be the > userspace developers. I cannot guarantee that fork will not be used in the > performance test of their workload since that is a normal posix call. Maybe a little > overthinking? :) If your application cares about performance, you either shouldn't be using fork(), or you should be using it very, very wisely (e.g., interaction with multi-threading, MADV_DONTFORK, avoid touching memory in parent until child completed). > I just think swap and COW are two main scenarios that may transparently split pmd sized > folios, so maybe we can solve it and make THP both reclaimable and stable. There is page migration, MADV_DONTNEED, munmap/mremap/madvise/mprotect in sub-2M blocks, memory failure handling and probably a lot more. THP allocation might fail. THP swapout+swapin might fail to allocate THPs. Tackling COW handling when you don't even know that it's a real problem seems premature. > Maybe > that can make THP more widely used in real deployed environment since the resource > can become more controllable for the users :). That's why I was thinking maybe > implementing it with setup switches is a reasonable solution? No magical toggles. > >> Note that an application that does fork() could use MADV_COLLAPSE after fork() >> to make sure that it immediately gets THPs back. >> >> There is also the option to just use MADV_DONTFORK to not even share ranges with >> a child process in the first place, avoiding page copies entirely. >> > > MADV_DONTFORK and MADV_COLLAPSE are nice and great options :), but the former one seems > to be a little wasteful :). It's actually the right thing to do (tm) if you care about fork() performance and know that your child will not actually need certain memory areas. For example, in QEMU we use it to exclude all guest memory from fork(), heavily improving fork() performance. [there are not a lot of fork() use cases left in QEMU today, fortunately] -- Cheers, David
在 Fri, May 01, 2026 at 08:30:39PM +0200,David Hildenbrand (Arm) 写道: Hi David, Thanks for replying again :). I've read your advices, and I agreed with your opinion, THP COW is premature now for the upstream, we can reconsider other approaches. :) > >> > >> Note that there was a recent related discussion for executable, which was rejected: > >> > >> https://lore.kernel.org/r/20251226100337.4171191-1-zhangqilong3@huawei.com > >> > > > > Yes, I know this history, and I know that it will cost some memory or latency, > > That’s why I was wondering maybe I can add a switch to it to make it > > configurable :). > > Switches for something like that is just not a good fit. > > For example, for a short-lived child (e.g., fork+exec) it usually makes no sense > to cow a larger chunk of address space, when you know that it will exit > immediately either way and free up the memory. > Yeah, for most workloads, fork will be used with a exec call soon, which makes the COW for THP not so useful for these workloads. > >>> > >>> In addition to the problem above, this logic can also generate some > >>> deficiency for THP itself. Currently THP is just a "best-effort" choice > >>> with no "certainty". THP is easily splitted into multiple small pages > >>> on common calling path like reclaiming, COW. A transparent splitting > >>> can cause throughput fluctuation for some workloads. For these workloads, > >>> we may want to give THP some "certainty" just like hugetlbfs, > >> > >> There are no such guarantees, though. And We wouldn't want to commit to any such > >> guarantees today. For example, simple page migration can split the folio. > >> Allocation failures will fallback to small pages etc. > >> > >> If you need guarantees, use hugetlb for now. > >> > > > > The reason why I want to use THP over hugetlb is that I need reclamation for my > > workload :). There are many processes in my workload that need 2M > > aligned folios for better performance, and we want to reclaim them back automatically > > when the process doesn’t need the folios. > > Can you share some details how exactly that is supposed to work? > Sorry for that, it's basically just a bunch of processes in the environment with 2M sized folio as their backend, but we want the OS to reclaim them when it's possible. And we want to balance performance and memory saving, and don't split so often since it may cause fraction and may influence the future 2M folio allocation. :) > > But hugetlbfs cannot do passive reclamation > > from what I know (except doing active madvise by the processes themselves). And using > > Right, you can only return hugetlb folios by doing MADV_DONTNEED or munmap(). > > > THP can easily split the hugepages. So that’s why I would like to add certainty for THP, > > Repeat after me: there are no guarantees. There is no certainty :) > > > and use THP for these processes as backend, because THP is very well integrated with > > the swap system and other filesystems. And from what I checked, > > it seems the most common case for splitting a THP is COW and swapping so I am trying > > to handle these two scenarios (But coincidentally, PMD swapping is committed in > > https://lore.kernel.org/all/D3F08F85-76E0-4C5A-ABA1-537C68E038B8@nvidia.com/ > > a few days before, which is a great implementation :) ). > > Right, but that really only changes how we map large folios, not how we allocate > them. There are no guarantees. > > > > >>> The effect > >>> we want is: after some customized setup, if only the system has usable > >>> folio, and the virtual memory alignment permits (or we setup to), we can > >>> make sure we always use THP for it, the system will never split it except > >>> the user wants to do so. > >>> > >>> This patchset is about both two things above, firstly we add pmd level > >>> THP COW support by revising the code in do_huge_pmd_wp_page, we added > >>> switch for it because different workloads may need different resources, > >> > >> The switch is bad, and we won't accept any toggle like that. A system-wide > >> setting does not make sense for such behavior. > >> > > > > Oh, the reason why I added a switch globally is also because the scenario I mentioned > > above, I want those processes to always use PMD sized folios as backend to make sure > > performance. > > "Always" is wishful thinking in many scenarios I'm afraid. Yeah, I agree, so we also think that maybe we can do some other things to increase the possibility of allocating pmd sized folios. :) > > > COW is truly not that common like swap out/swap in, it just can happen > > sometimes, which I guess the reason may be about image duplication. Setting the system > > globally is more convenient for my situation :). I can go without this global switch > > if it's more reasonable. > > > >> A per-VMA flag? Maybe, but I expect pushback as well, as it is way too specific. > >> So we'd have to find some concept that abstracts these semantics. But I expect > >> pushback as well. > >> > >> We messed up enough with toggles in THP space, unfortunately. > >> > >> Also, anything that only works for PMD-sized THPs is a warning sign in 2026 :) > >> > > > > And for PMD-sized THPs, actually, I’m also considering adding more support for > > COW to mTHP if the upstream consider it useful. And also for pud sized THPs > > also. But I guess I have to firstly handle stage 1: PMD level COW now :). > > And also, since PMD sized folio is commonly used in my workload, I'm also wondering > > digging into pmd sized KSM in the future, in which I think pmd sized COW may > > be more useful then :). > > I'm afraid I have to stop you right there: there has to be a pretty convincing > story to add any of that. In particular KSM with large folios (/me shivering). > > But I already don't buy the COW story. Just configure khugepaged in a better way > or use MADV_COLLAPSE and you don't really need to modify the kernel at all in > 99.99% of the case. (khugepaged needs a lot of tuning work, it's currently not > the smartest implementation) > > Maybe, we might give khugepaged better direction of what to try scanning next > (e.g., where we just COW'ed a THP). Not sure, there are plenty of things to explore. > I see, actually all the things here is about saving memory and keeping the 2M tlb benifit at the same time, including my KSM thought and THP COW. Giving khugepaged a better direction is also what we are considering in the next step :). > > > >> You don't really raise any concrete use cases or performance numbers for these > >> use cases. Some details about applications that use fork() and rely on such > >> behavior would be helpful. > >> > > > > Sorry for that, the concrete workload itself hasn't been finished yet. > > Okay, what I thought after seeing no workloads an no performance numbers :) > > If you don't know the workload, how can you claim that the additional latency > and/or memory consumption is not a problem? > Oh, our point here is that we don't want these 2M folios to be split, since that may cause some fraction and increases the difficulty a little for allocating a 2M folio later as the time goes on. It may cause some additional latency and memory consumption, that's true, but the workload we are trying to do wants to make sure the 2M folios in the system first. And though the workload hasn't been finished, we analyzed what it will do, and we think for the most time, COWed 2M folio will be written in a range much bigger than 4K. So it will will cause more page faults if the fault size is 4K :). As we see, the time consuming of only one 2M sized page fault should be able to beat the multiple 4K sized page fault, since the copy can be merged together and there are not so many times of handling other than copy. We can see a performance improvement in the end to end copying test which includes many possible COW on 2M pages, though. The performance improvement is about 2 times. The improvement is not big as we imagine it should be. We'll dig into more details then. :) > Also: there is no guarantee that you will actually succeed in allocating a PMD > THP during a COW fault. > Yes, so we are actually also considering some solutions to increase the success rate of allocating PMD sized THP like reserving, the idea comes from TAO of Yu Zhao in https://lore.kernel.org/all/20240229183436.4110845-1-yuzhao@google.com/. But we may do the reserving using another approach like migratetype though since that may make it easier to dynamically change the reserving size. We also want to discuss it with the upstream about this if anyone has time :). > > > Now it just > > can happen sometimes in my multi-2M-sized-processes workload test. But the user > > of our 2M sized folio schema is actually not necessarily myself but also can be the > > userspace developers. I cannot guarantee that fork will not be used in the > > performance test of their workload since that is a normal posix call. Maybe a little > > overthinking? :) > > If your application cares about performance, you either shouldn't be using > fork(), or you should be using it very, very wisely (e.g., interaction with > multi-threading, MADV_DONTFORK, avoid touching memory in parent until child > completed). > Agreed, we'll try that, thank you. :) > > I just think swap and COW are two main scenarios that may transparently split pmd sized > > folios, so maybe we can solve it and make THP both reclaimable and stable. > > There is page migration, MADV_DONTNEED, munmap/mremap/madvise/mprotect in sub-2M > blocks, memory failure handling and probably a lot more. THP allocation might > fail. THP swapout+swapin might fail to allocate THPs. > Yes, in my former consideration I just thought munmap/mremap/madvise/mprotect are actively called, so it should be easier to restrict them, like directly fail them when the user calls them in sub-2M blocks. Page migration is truly another scenorio we need to handle. But it's not that likely to happen if we configure it so since we have our CONFIG_ARCH_ENABLE_THP_MIGRATION setup, things like numa balancing, live migration, compaction can all be disabled though. Maybe there is something I missed here? :) > Tackling COW handling when you don't even know that it's a real problem seems > premature. > Yeah, I guess THP COW can be replaced by other approaches. We just think maybe we can firstly add the THP COW, then the COW that may happen in fork, pmd swap in, and maybe other places can all benifit from it. But maybe it's still not the only way to solve the problem we meet right now. > > Maybe > > that can make THP more widely used in real deployed environment since the resource > > can become more controllable for the users :). That's why I was thinking maybe > > implementing it with setup switches is a reasonable solution? > > No magical toggles. > > > > >> Note that an application that does fork() could use MADV_COLLAPSE after fork() > >> to make sure that it immediately gets THPs back. > >> > >> There is also the option to just use MADV_DONTFORK to not even share ranges with > >> a child process in the first place, avoiding page copies entirely. > >> > > > > MADV_DONTFORK and MADV_COLLAPSE are nice and great options :), but the former one seems > > to be a little wasteful :). > > It's actually the right thing to do (tm) if you care about fork() performance > and know that your child will not actually need certain memory areas. > > For example, in QEMU we use it to exclude all guest memory from fork(), heavily > improving fork() performance. [there are not a lot of fork() use cases left in > QEMU today, fortunately] > Yeah, these two are nice in many situations, we'll consider using MADV_DONTFORK and MADV_COLLAPSE and other tools for our workload, thanks! > -- > Cheers, > > David Best regards, Luka
syzbot ci has tested the following series [v1] mm: Support selecting doing direct COW for anonymous pmd entry https://lore.kernel.org/all/20260501-thp_cow-v1-0-005377483738@tencent.com * [PATCH 1/5] mm: add basic madvise helpers and branch for THP setup * [PATCH 2/5] mm: add pmd level THP COW parameter in sysfs * [PATCH 3/5] mm: add pmd level THP COW judgement helpers * [PATCH 4/5] mm: enable map_anon_folio_pmd_nopf to handle unshare * [PATCH 5/5] mm: support choosing to do THP COW for anonymous pmd entry. and found the following issue: general protection fault in __page_table_check_pmds_set Full report is available here: https://ci.syzbot.org/series/37e78e03-c08b-4de1-9b07-a21c64f4f462 *** general protection fault in __page_table_check_pmds_set tree: mm-new URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/akpm/mm.git base: 41cd9e3d23b8fd9e6c3c0311e9cb0304442c6141 arch: amd64 compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 config: https://ci.syzbot.org/builds/fcdd679f-29e8-43db-8792-d4fd97c62d91/config syz repro: https://ci.syzbot.org/findings/87820d58-5d91-4c2e-b80b-5a75006e230d/syz_repro Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 0 UID: 0 PID: 5807 Comm: syz.1.18 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 RIP: 0010:__page_table_check_pmds_set+0x1d4/0x340 mm/page_table_check.c:240 Code: 00 00 4c 89 6c 24 08 4c 89 3c 24 4a 8d 2c fd f8 ff ff ff 31 db 49 bf 00 00 00 00 00 fc ff df 49 8d 3c 1e 48 89 f8 48 c1 e8 03 <42> 80 3c 38 00 74 05 e8 00 29 f4 ff 4d 8b 24 1e 45 89 e5 41 81 e5 RSP: 0018:ffffc90003c46ee0 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: ffff8881102a1d80 RSI: 0000000000000001 RDI: 0000000000000000 RBP: 0000000000000000 R08: 0000000000000001 R09: 1ffff110242081d8 R10: dffffc0000000000 R11: ffffed10242081d9 R12: dffffc0000000000 R13: 0000000025c008e7 R14: 0000000000000000 R15: dffffc0000000000 FS: 00007fbef78216c0(0000) GS:ffff88818dc91000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000001b32f63fff CR3: 000000002350a000 CR4: 00000000000006f0 Call Trace: <TASK> page_table_check_pmds_set include/linux/page_table_check.h:92 [inline] set_pmd_at arch/x86/include/asm/pgtable.h:1209 [inline] map_anon_folio_pmd_nopf+0x452/0x480 mm/huge_memory.c:1449 collapse_huge_page mm/khugepaged.c:1411 [inline] mthp_collapse mm/khugepaged.c:1530 [inline] collapse_scan_pmd mm/khugepaged.c:1773 [inline] collapse_single_pmd+0x4691/0x5540 mm/khugepaged.c:2786 madvise_collapse+0x300/0x7a0 mm/khugepaged.c:3218 madvise_vma_behavior+0x11b0/0x4210 mm/madvise.c:1383 madvise_walk_vmas+0x573/0xae0 mm/madvise.c:1738 madvise_do_behavior+0x386/0x540 mm/madvise.c:1954 do_madvise+0x1fa/0x2e0 mm/madvise.c:2047 __do_sys_madvise mm/madvise.c:2056 [inline] __se_sys_madvise mm/madvise.c:2054 [inline] __x64_sys_madvise+0xa6/0xc0 mm/madvise.c:2054 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fbef699cdd9 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fbef7821028 EFLAGS: 00000246 ORIG_RAX: 000000000000001c RAX: ffffffffffffffda RBX: 00007fbef6c15fa0 RCX: 00007fbef699cdd9 RDX: 0000000000000019 RSI: 0000000000400000 RDI: 0000200000000000 RBP: 00007fbef6a32d69 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007fbef6c16038 R14: 00007fbef6c15fa0 R15: 00007ffdddd640f8 </TASK> Modules linked in: ---[ end trace 0000000000000000 ]--- RIP: 0010:__page_table_check_pmds_set+0x1d4/0x340 mm/page_table_check.c:240 Code: 00 00 4c 89 6c 24 08 4c 89 3c 24 4a 8d 2c fd f8 ff ff ff 31 db 49 bf 00 00 00 00 00 fc ff df 49 8d 3c 1e 48 89 f8 48 c1 e8 03 <42> 80 3c 38 00 74 05 e8 00 29 f4 ff 4d 8b 24 1e 45 89 e5 41 81 e5 RSP: 0018:ffffc90003c46ee0 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: ffff8881102a1d80 RSI: 0000000000000001 RDI: 0000000000000000 RBP: 0000000000000000 R08: 0000000000000001 R09: 1ffff110242081d8 R10: dffffc0000000000 R11: ffffed10242081d9 R12: dffffc0000000000 R13: 0000000025c008e7 R14: 0000000000000000 R15: dffffc0000000000 FS: 00007fbef78216c0(0000) GS:ffff88818dc91000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000001b32f63fff CR3: 000000002350a000 CR4: 00000000000006f0 ---------------- Code disassembly (best guess): 0: 00 00 add %al,(%rax) 2: 4c 89 6c 24 08 mov %r13,0x8(%rsp) 7: 4c 89 3c 24 mov %r15,(%rsp) b: 4a 8d 2c fd f8 ff ff lea -0x8(,%r15,8),%rbp 12: ff 13: 31 db xor %ebx,%ebx 15: 49 bf 00 00 00 00 00 movabs $0xdffffc0000000000,%r15 1c: fc ff df 1f: 49 8d 3c 1e lea (%r14,%rbx,1),%rdi 23: 48 89 f8 mov %rdi,%rax 26: 48 c1 e8 03 shr $0x3,%rax * 2a: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1) <-- trapping instruction 2f: 74 05 je 0x36 31: e8 00 29 f4 ff call 0xfff42936 36: 4d 8b 24 1e mov (%r14,%rbx,1),%r12 3a: 45 89 e5 mov %r12d,%r13d 3d: 41 rex.B 3e: 81 .byte 0x81 3f: e5 .byte 0xe5 *** If these findings have caused you to resend the series or submit a separate fix, please add the following tag to your commit message: Tested-by: syzbot@syzkaller.appspotmail.com --- This report is generated by a bot. It may contain errors. syzbot ci engineers can be reached at syzkaller@googlegroups.com. To test a patch for this bug, please reply with `#syz test` (should be on a separate line). The patch should be attached to the email. Note: arguments like custom git repos and branches are not supported.
© 2016 - 2026 Red Hat, Inc.