s390/mm: Batch PTE updates in lazy MMU mode
Subsystems (8)
Alexander Gordeev
maintainercover s390/mm: Batch PTE updates in lazy MMU mode
Hi All!
This series addresses an s390-specific aspect of how page table entries are modified. In many cases, changing a valid PTE (for example, setting or clearing a hardware bit) requires issuing an Invalidate Page Table Entry (IPTE) instruction beforehand.
A disadvantage of the IPTE instruction is that it may initiate a machine-wide quiesce state. This state acts as an expensive global hardware lock and should be avoided whenever possible.
Currently, IPTE is invoked for each individual PTE update in most code paths. However, the instruction itself supports invalidating multiple PTEs at once, covering up to 256 entries. Using this capability can significantly reduce the number of quiesce events, with a positive impact on overall system performance. At present, this feature is not utilized.
An effort was therefore made to identify kernel code paths that update large numbers of consecutive PTEs. Such updates can be batched and handled by a single IPTE invocation, leveraging the hardware support described above.
A natural candidate for this optimization is page-table walkers that change attributes of memory ranges and thus modify contiguous ranges of PTEs. Many memory-management system calls enter lazy MMU mode while updating such ranges.
This lazy MMU mode can be leveraged to build on the already existing infrastructure and implement a software-level lazy MMU mechanism, allowing expensive PTE invalidations on s390 to be batched.
Patch #2 is the actual s390 rework, while patch #1 is a prerequisite that affects the generic code. It was briefly communicated upstream and got some approvals:
https://lore.kernel.org/linux-mm/[email protected]/#t
https://lore.kernel.org/linux-mm/[email protected]/
I am sending this as an RFC in the hope that I may have missed less obvious existing code paths or potential code changes that could further enable batching. Any ideas would be greatly appreciated!
Thanks!
Alexander Gordeev (2):
mm: make lazy MMU mode context-aware
s390/mm: Batch PTE updates in lazy MMU mode
arch/s390/Kconfig | 8 + arch/s390/include/asm/pgtable.h | 209 +++++++++++++++-- arch/s390/mm/Makefile | 1 + arch/s390/mm/ipte_batch.c | 396 ++++++++++++++++++++++++++++++++ arch/s390/mm/pgtable.c | 8 +- fs/proc/task_mmu.c | 2 +- include/linux/pgtable.h | 42 ++++ mm/madvise.c | 8 +- mm/memory.c | 8 +- mm/mprotect.c | 2 +- mm/mremap.c | 2 +- mm/vmalloc.c | 6 +- 12 files changed, 659 insertions(+), 33 deletions(-) create mode 100644 arch/s390/mm/ipte_batch.c
Signature · 1 line
2.51.0
Alexander Gordeev
maintainerRFC PATCH 1/2 mm: make lazy MMU mode context-aware
Lazy MMU mode is assumed to be context-independent, in the sense that it does not need any additional information while operating. However, the s390 architecture benefits from knowing the exact page table entries being modified.
Introduce lazy_mmu_mode_enable_pte(), which is provided with the process address space and the page table being operated on. This information is required to enable s390-specific optimizations.
The function takes parameters that are typically passed to page- table level walkers, which implies that the span of PTE entries never crosses a page table boundary.
Architectures that do not require such information simply do not need to define the arch_enter_lazy_mmu_mode_pte() callback.
--- fs/proc/task_mmu.c | 2 +- include/linux/pgtable.h | 42 +++++++++++++++++++++++++++++++++++++++++ mm/madvise.c | 8 ++++---- mm/memory.c | 8 ++++---- mm/mprotect.c | 2 +- mm/mremap.c | 2 +- mm/vmalloc.c | 6 +++--- 7 files changed, 56 insertions(+), 14 deletions(-)
fs/proc/task_mmu.c +1 -1
@@ -2752,7 +2752,7 @@ static int pagemap_scan_pmd_entry(pmd_t *pmd, unsigned long start,return 0 ; } -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (vma->vm_mm, start, end, start_pte);if ((p->arg.flags &PM_SCAN_WP_MATCHING ) && !p->vec_out) {/* Fast path for performing exclusive WP */
include/linux/pgtable.h +42 -0
54 lines not on this page · raw message
mm/madvise.c +4 -4
@@ -451,7 +451,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,if (!start_pte)return 0 ;flush_tlb_batched_pending (mm); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, addr, end, start_pte);for (; addr < end; pte += nr, addr += nr *PAGE_SIZE ) { nr =1 ; ptent =ptep_get (pte);
@@ -506,7 +506,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,if (!start_pte)break ;flush_tlb_batched_pending (mm); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, addr, end, start_pte);if (!err) nr =0 ;continue ;
@@ -673,7 +673,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,if (!start_pte)return 0 ;flush_tlb_batched_pending (mm); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, addr, end, start_pte);for (; addr != end; pte += nr, addr +=PAGE_SIZE * nr) { nr =1 ; ptent =ptep_get (pte);
@@ -733,7 +733,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,if (!start_pte)break ;flush_tlb_batched_pending (mm); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, addr, end, pte);if (!err) nr =0 ;continue ;
mm/memory.c +4 -4
@@ -1269,7 +1269,7 @@ copy_pte_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,spin_lock_nested (src_ptl,SINGLE_DEPTH_NESTING ); orig_src_pte = src_pte; orig_dst_pte = dst_pte; -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (src_mm, addr, end, src_pte); do { nr = 1;
@@ -1917,7 +1917,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,return addr;flush_tlb_batched_pending (mm); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, addr, end, start_pte); do { bool any_skipped = false;
@@ -2875,7 +2875,7 @@ static int remap_pte_range(struct mm_struct *mm, pmd_t *pmd, mapped_pte = pte =pte_alloc_map_lock (mm, pmd, addr, &ptl);if (!pte)return -ENOMEM ; -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, addr, end, mapped_pte); do { BUG_ON(!pte_none(ptep_get(pte))); if (!pfn_modify_allowed(pfn, prot)) {
@@ -3235,7 +3235,7 @@ static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd,return -EINVAL ; } -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, addr, end, mapped_pte);if (fn) { do {
mm/mprotect.c +1 -1
@@ -233,7 +233,7 @@ static long change_pte_range(struct mmu_gather *tlb, is_private_single_threaded =vma_is_single_threaded_private (vma);flush_tlb_batched_pending (vma->vm_mm); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (vma->vm_mm, addr, end, pte); do { nr_ptes = 1; oldpte = ptep_get(pte);
mm/mremap.c +1 -1
@@ -260,7 +260,7 @@ static int move_ptes(struct pagetable_move_control *pmc,if (new_ptl != old_ptl)spin_lock_nested (new_ptl,SINGLE_DEPTH_NESTING );flush_tlb_batched_pending (vma->vm_mm); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (mm, old_addr, old_end, old_ptep);for (; old_addr < old_end; old_ptep += nr_ptes, old_addr += nr_ptes *PAGE_SIZE , new_ptep += nr_ptes, new_addr += nr_ptes *PAGE_SIZE ) {
mm/vmalloc.c +3 -3
@@ -108,7 +108,7 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,if (!pte)return -ENOMEM ; -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (&init_mm, addr, end, pte); do { if (unlikely(!pte_none(ptep_get(pte)))) {
@@ -371,7 +371,7 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,unsigned long size =PAGE_SIZE ; pte =pte_offset_kernel (pmd, addr); -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (&init_mm, addr, end, pte); do { #ifdef CONFIG_HUGETLB_PAGE
@@ -538,7 +538,7 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,if (!pte)return -ENOMEM ; -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (&init_mm, addr, end, pte); do { struct page *page = pages[*nr];
Signature · 1 line
2.51.0
David Hildenbrand (Arm)
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 45 lines · Alexander Gordeev
Lazy MMU mode is assumed to be context-independent, in the sense that it does not need any additional information while operating. However, the s390 architecture benefits from knowing the exact page table entries being modified.
Introduce
lazy_mmu_mode_enable_pte(), which is provided with the process address space and the page table being operated on. This information is required to enable s390-specific optimizations.The function takes parameters that are typically passed to page- table level walkers, which implies that the span of PTE entries never crosses a page table boundary.
Architectures that do not require such information simply do not need to define the
arch_enter_lazy_mmu_mode_pte()callback.Signed-off-by Alexander Gordeev <[email protected]>--- fs/proc/task_mmu.c | 2 +- include/linux/pgtable.h | 42 +++++++++++++++++++++++++++++++++++++++++ mm/madvise.c | 8 ++++---- mm/memory.c | 8 ++++---- mm/mprotect.c | 2 +- mm/mremap.c | 2 +- mm/vmalloc.c | 6 +++--- 7 files changed, 56 insertions(+), 14 deletions(-)fs/proc/task_mmu.c +1
-1@@ -2752,7 +2752,7 @@ static int pagemap_scan_pmd_entry(pmd_t *pmd, unsigned long start,return 0 ; } -lazy_mmu_mode_enable (); +lazy_mmu_mode_enable_pte (vma->vm_mm, start, end, start_pte);if ((p->arg.flags &PM_SCAN_WP_MATCHING ) && !p->vec_out) {/* Fast path for performing exclusive WP */ include/linux/pgtable.h +0
-0
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 278
Quoted 8 lines
include/linux/pgtable.h +5
-0@@ -271,6 +271,44 @@ static inline void lazy_mmu_mode_enable(void)arch_enter_lazy_mmu_mode (); } +#ifndef arch_enter_lazy_mmu_mode_pte +static inline void arch_enter_lazy_mmu_mode_pte (struct mm_struct *mm, +unsigned long addr, +unsigned long end, +pte_t *ptep)
Two tab alignment please. (applies to other things hwere as well)
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 285
Quoted 7 lines
include/linux/pgtable.h +7
-0+{ +arch_enter_lazy_mmu_mode (); +} +#endif + +/** +* lazy_mmu_mode_enable_pte() - Enable the lazy MMU mode with parameters
You have to be a lot clearer about implications. For example, what happens if we would bail out and not process all ptes? What are the exact semantics.
Quoted 4 lines
include/linux/pgtable.h +4
-0+* +* Enters a new lazy MMU mode section; if the mode was not already enabled, +* enables it and calls arch_enter_lazy_mmu_mode_pte(). +*
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 299
Quoted 10 lines
include/linux/pgtable.h +10
-0+* Must be paired with a call to lazy_mmu_mode_disable(). +* +* Has no effect if called: +* - While paused - see lazy_mmu_mode_pause() +* - In interrupt context +*/ +static inline void lazy_mmu_mode_enable_pte (struct mm_struct *mm, +unsigned long addr, +unsigned long end, +pte_t *ptep)
It can be multiple ptes, so should this be some kind of "pte_range"/
lazy_mmu_mode_enable_for_pte_range()
A bit mouthful but clearer.
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 310
Quoted 11 lines
include/linux/pgtable.h +11
-0+{ +struct lazy_mmu_state *state = ¤t->lazy_mmu_state; + +if (in_interrupt () || state->pause_count >0 ) +return ; + +VM_WARN_ON_ONCE (state->enable_count ==U8_MAX ); + +if (state->enable_count++ ==0 ) +arch_enter_lazy_mmu_mode_pte (mm, addr, end, ptep); +}
I'm wondering whether that could instead be some optional interface that we trigger after the lazy_mmu_mode_enable. But looking at lazy_mmu_mode_enable() users, there don't seem to be cases where we would process multiple different ranges under a single enable() call, right?
Signature · 3 lines
Cheers, David
Alexander Gordeev
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
On Wed, Mar 25, 2026 at 10:55:23AM +0100, David Hildenbrand (Arm) wrote:
Hi David,
Quoted 6 lines
Quoted 2 lines
+/**
+ *lazy_mmu_mode_enable_pte()- Enable the lazy MMU mode with parametersYou have to be a lot clearer about implications. For example, what happens if we would bail out and not process all ptes? What are the exact semantics.
The only implication is "only this address/PTE range could be updated and that range may span one page table at most".
Whether all or portion of PTEs were actually updated is not defined, just like in case of lazy_mmu_mode_enable_pte().
Makes sense?
Quoted 20 lines
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 299
Quoted 13 lines
+ * Enters a new lazy MMU mode section; if the mode was not already enabled,
+ * enables it and callsarch_enter_lazy_mmu_mode_pte().
+ *
+ * Must be paired with a call tolazy_mmu_mode_disable().
+ *
+ * Has no effect if called:
+ * - While paused - seelazy_mmu_mode_pause()
+ * - In interrupt context
+ */include/linux/pgtable.h +4
-0+static inline void lazy_mmu_mode_enable_pte (struct mm_struct *mm, +unsigned long addr, +unsigned long end, +pte_t *ptep)It can be multiple ptes, so should this be some kind of "pte_range"/
lazy_mmu_mode_enable_for_pte_range()A bit mouthful but clearer.
Quoted 10 lines
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 309
Quoted 10 lines
include/linux/pgtable.h +10
-0+{ +struct lazy_mmu_state *state = ¤t->lazy_mmu_state; + +if (in_interrupt () || state->pause_count >0 ) +return ; + +VM_WARN_ON_ONCE (state->enable_count ==U8_MAX ); + +if (state->enable_count++ ==0 ) +arch_enter_lazy_mmu_mode_pte (mm, addr, end, ptep);
I will also change arch_enter_lazy_mmu_mode_pte() to arch_enter_lazy_mmu_mode_for_pte_range() then.
Quoted 4 lines
Quoted 1 line
+}
I'm wondering whether that could instead be some optional interface that we trigger after the
lazy_mmu_mode_enable. But looking at
To me just two separate and (as you put it) mouthful names appeal better than an optional follow-up interface.
Quoted 2 lines
lazy_mmu_mode_enable()users, there don't seem to be cases where we would process multiple different ranges under a singleenable()call, right?
Multiple different ranges still could be processed, but then one should continue using arch_enter_lazy_mmu_mode(). E.g. these were less obvious than traditional walkers and left them intact:
mm/migrate_device.c
mm/tests/lazy_mmu_mode_kunit.c
mm/userfaultfd.c
mm/vmscan.c
Quoted 4 lines
Signature · 3 lines
Cheers, David
Thanks for the quick review!
Alexander Gordeev
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 2 lines
Multiple different ranges still could be processed, but then one should continue using
arch_enter_lazy_mmu_mode().
lazy_mmu_mode_enable()
Re: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
In reply to Alexander GordeevQuoted 9 lines · Alexander Gordeev
On Wed, Mar 25, 2026 at 10:55:23AM +0100, David Hildenbrand (Arm) wrote:
Hi David,
Quoted 5 lines
Quoted 2 lines
+/**
+ *lazy_mmu_mode_enable_pte()- Enable the lazy MMU mode with parametersYou have to be a lot clearer about implications. For example, what happens if we would bail out and not process all ptes? What are the exact semantics.
Quoted 7 lines
The only implication is "only this address/PTE range could be updated and that range may span one page table at most".
Whether all or portion of PTEs were actually updated is not defined, just like in case of
lazy_mmu_mode_enable_pte().Makes sense?
I also feel that the comment needs to be much more specific. From a brief glance at patch 2, it seems that __ipte_batch_set_pte() assumes that all PTEs processed after this function is called are contiguous. This should be documented.
Quoted 19 lines
Quoted 19 lines
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 299
Quoted 13 lines
+ * Enters a new lazy MMU mode section; if the mode was not already enabled,
+ * enables it and callsarch_enter_lazy_mmu_mode_pte().
+ *
+ * Must be paired with a call tolazy_mmu_mode_disable().
+ *
+ * Has no effect if called:
+ * - While paused - seelazy_mmu_mode_pause()
+ * - In interrupt context
+ */include/linux/pgtable.h +4
-0+static inline void lazy_mmu_mode_enable_pte (struct mm_struct *mm, +unsigned long addr, +unsigned long end, +pte_t *ptep)It can be multiple ptes, so should this be some kind of "pte_range"/
lazy_mmu_mode_enable_for_pte_range()A bit mouthful but clearer.
Quoted 12 lines
Quoted 10 lines
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 309
Quoted 10 lines
include/linux/pgtable.h +10
-0+{ +struct lazy_mmu_state *state = ¤t->lazy_mmu_state; + +if (in_interrupt () || state->pause_count >0 ) +return ; + +VM_WARN_ON_ONCE (state->enable_count ==U8_MAX ); + +if (state->enable_count++ ==0 ) +arch_enter_lazy_mmu_mode_pte (mm, addr, end, ptep);I will also change
arch_enter_lazy_mmu_mode_pte()toarch_enter_lazy_mmu_mode_for_pte_range()then.
Makes sense. The interface looks reasonable to me with this new name.
One more comment though: in previous discussions you mentioned the need for arch_{pause,resume} hooks, is that no longer necessary simply because {pause,resume} are not used on the paths where you make use of the new enable function?
- Kevin
Alexander Gordeev
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 13 lines · Kevin Brodsky
Quoted 12 lines · Alexander Gordeev
On Wed, Mar 25, 2026 at 10:55:23AM +0100, David Hildenbrand (Arm) wrote:
Hi David,
Quoted 5 lines
Quoted 2 lines
+/**
+ *lazy_mmu_mode_enable_pte()- Enable the lazy MMU mode with parametersYou have to be a lot clearer about implications. For example, what happens if we would bail out and not process all ptes? What are the exact semantics.
The only implication is "only this address/PTE range could be updated and that range may span one page table at most".
Quoted 8 lines
Quoted 4 lines
Whether all or portion of PTEs were actually updated is not defined, just like in case of
lazy_mmu_mode_enable_pte().Makes sense?
I also feel that the comment needs to be much more specific. From a brief glance at patch 2, it seems that
__ipte_batch_set_pte()assumes that all PTEs processed after this function is called are contiguous.
No, this is actually not the case. __ipte_batch_set_pte() just sets ceilings for later processing. The PTEs within the range could be updated in any order and not necessarily all of them.
Quoted 1 line
This should be documented.
Will do.
Quoted 9 lines
Quoted 2 lines
I will also change
arch_enter_lazy_mmu_mode_pte()toarch_enter_lazy_mmu_mode_for_pte_range()then.Makes sense. The interface looks reasonable to me with this new name.
One more comment though: in previous discussions you mentioned the need for arch_{pause,resume} hooks, is that no longer necessary simply because {pause,resume} are not used on the paths where you make use of the new enable function?
Yes. I did implement arch_pause|resume_lazy_mmu_mode() for a custom KASAN sanitizer to catch direct PTE dereferences - ones that bypass ptep_get()/set_pte() in lazy mode.
But that code is not upstreamed and therefore there is no need to introduce the hooks just right now.
Quoted 1 line
- Kevin
Thanks for the review, Kevin!
Re: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 17 lines · Alexander Gordeev
Quoted 16 lines · Kevin Brodsky
Quoted 15 lines · Alexander Gordeev
On Wed, Mar 25, 2026 at 10:55:23AM +0100, David Hildenbrand (Arm) wrote:
Hi David,
Quoted 5 lines
Quoted 2 lines
+/**
+ *lazy_mmu_mode_enable_pte()- Enable the lazy MMU mode with parametersYou have to be a lot clearer about implications. For example, what happens if we would bail out and not process all ptes? What are the exact semantics.
The only implication is "only this address/PTE range could be updated and that range may span one page table at most".
Whether all or portion of PTEs were actually updated is not defined, just like in case of
lazy_mmu_mode_enable_pte().
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Makes sense?
I also feel that the comment needs to be much more specific. From a brief glance at patch 2, it seems that
__ipte_batch_set_pte()assumes that all PTEs processed after this function is called are contiguous.No, this is actually not the case.
__ipte_batch_set_pte()just sets ceilings for later processing. The PTEs within the range could be updated in any order and not necessarily all of them.
Fair enough, I suppose what I tried to say is that all PTEs must be in a certain range :)
Quoted 7 lines
Quoted 1 line
This should be documented.
Will do.
Quoted 4 lines
Quoted 2 lines
I will also change
arch_enter_lazy_mmu_mode_pte()toarch_enter_lazy_mmu_mode_for_pte_range()then.Makes sense. The interface looks reasonable to me with this new name.
Quoted 7 lines
Quoted 4 lines
One more comment though: in previous discussions you mentioned the need for arch_{pause,resume} hooks, is that no longer necessary simply because {pause,resume} are not used on the paths where you make use of the new enable function?
Yes. I did implement arch_pause|resume_lazy_mmu_mode() for a custom KASAN sanitizer to catch direct PTE dereferences - ones that bypass ptep_get()/set_pte() in lazy mode.
That sounds pretty useful!
Quoted 2 lines
But that code is not upstreamed and therefore there is no need to introduce the hooks just right now.
Ack that makes sense.
Quoted 2 lines
Quoted 1 line
- Kevin
Thanks for the review, Kevin!
No problem :)
- Kevin
David Hildenbrand (Arm)
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
In reply to Alexander GordeevQuoted 7 lines · Alexander Gordeev
On Wed, Mar 25, 2026 at 10:55:23AM +0100, David Hildenbrand (Arm) wrote:
Hi David,
Quoted 3 lines
Quoted 2 lines
+/**
+ *lazy_mmu_mode_enable_pte()- Enable the lazy MMU mode with parameters
Quoted 6 lines
Quoted 3 lines
You have to be a lot clearer about implications. For example, what happens if we would bail out and not process all ptes? What are the exact semantics.
The only implication is "only this address/PTE range could be updated and that range may span one page table at most".
Probably phrase it stronger. "No ptes outside of this range must be updated" etc.
Quoted 3 lines
Whether all or portion of PTEs were actually updated is not defined, just like in case of
lazy_mmu_mode_enable_pte().
Okay, then let's document that.
Quoted 3 lines
Makes sense?
Yes.
Quoted 34 lines
Quoted 20 lines
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 299
Quoted 13 lines
+ * Enters a new lazy MMU mode section; if the mode was not already enabled,
+ * enables it and callsarch_enter_lazy_mmu_mode_pte().
+ *
+ * Must be paired with a call tolazy_mmu_mode_disable().
+ *
+ * Has no effect if called:
+ * - While paused - seelazy_mmu_mode_pause()
+ * - In interrupt context
+ */include/linux/pgtable.h +4
-0+static inline void lazy_mmu_mode_enable_pte (struct mm_struct *mm, +unsigned long addr, +unsigned long end, +pte_t *ptep)It can be multiple ptes, so should this be some kind of "pte_range"/
lazy_mmu_mode_enable_for_pte_range()A bit mouthful but clearer.
Quoted 10 lines
include/linux/pgtable.h static inline void lazy_mmu_mode_enable(void) · line 309
Quoted 10 lines
include/linux/pgtable.h +10
-0+{ +struct lazy_mmu_state *state = ¤t->lazy_mmu_state; + +if (in_interrupt () || state->pause_count >0 ) +return ; + +VM_WARN_ON_ONCE (state->enable_count ==U8_MAX ); + +if (state->enable_count++ ==0 ) +arch_enter_lazy_mmu_mode_pte (mm, addr, end, ptep);I will also change
arch_enter_lazy_mmu_mode_pte()toarch_enter_lazy_mmu_mode_for_pte_range()then.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
+}
I'm wondering whether that could instead be some optional interface that we trigger after the
lazy_mmu_mode_enable. But looking atTo me just two separate and (as you put it) mouthful names appeal better than an optional follow-up interface.
Yes, probably better.
Signature · 3 lines
Cheers, David
Alexander Gordeev
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 7 lines · David Hildenbrand (Arm)
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines
+ *
lazy_mmu_mode_enable_pte()- Enable the lazy MMU mode with parametersYou have to be a lot clearer about implications. For example, what happens if we would bail out and not process all ptes? What are the exact semantics.
Quoted 5 lines
Quoted 2 lines
The only implication is "only this address/PTE range could be updated and that range may span one page table at most".
Probably phrase it stronger. "No ptes outside of this range must be updated" etc.
That turns out to be bit more complicated. The below cases do not fit such a strong requirement:
1. copy_pte_range() operates on two ranges: source and destination. Though lazy_mmu_mode_enable_for_pte_range() applies to the source one, updates to the destination are still happen while in tha lazy mode. (Although the lazy mode is not actually needed for the destination unattached MM).
2. move_ptes() also operates on a source and destination ranges, but unlike copy_pte_range() the destination range is also attached to the currently active task.
3. Though theoretical, nesting sections with interleaving calls to lazy_mmu_mode_enable() and lazy_mmu_mode_enable_for_pte_range() make it difficult to define (let alone to implement) which range is currently active, if any.
All of these goes away if we switch from for_pte_range() to fast_pte_range() semantics:
/**
* lazy_mmu_mode_enable_fast_pte_range() - Enable the lazy MMU mode with fast updates.
* @mm: Address space the ptes represent.
* @addr: Address of the first pte.
* @end: End address of the range.
* @ptep: Page table pointer for the first entry.
*
* Enters a new lazy MMU mode section and allows fast updates for PTEs
* within the specified range, while PTEs outside of the range are
* updated in the normal way - as if lazy_mmu_mode_enable() was called;
* if lazy MMU mode was not already enabled, enables it and calls
* arch_enter_lazy_mmu_mode_fast_pte_range(); if the mode was already
* enabled, the provided PTE range is ignored.
*
* The PTE range must belong to the provided memory space and must
* not cross a page table boundary.
*
* There are no requirements on the order or range completeness of PTE updates. *
* Must be paired with a call to lazy_mmu_mode_disable().
*
* Has no effect if called:
* - while paused (see lazy_mmu_mode_pause())
* - in interrupt context
*/
Thoughts?
Thanks!
David Hildenbrand (Arm)
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 8 lines · Alexander Gordeev
Quoted 6 lines · David Hildenbrand (Arm)
Quoted 3 lines
The only implication is "only this address/PTE range could be updated and that range may span one page table at most".
Probably phrase it stronger. "No ptes outside of this range must be updated" etc.
Quoted 8 lines
That turns out to be bit more complicated. The below cases do not fit such a strong requirement:
1.
copy_pte_range()operates on two ranges: source and destination. Thoughlazy_mmu_mode_enable_for_pte_range()applies to the source one, updates to the destination are still happen while in tha lazy mode. (Although the lazy mode is not actually needed for the destination unattached MM).
So, here a
"No ptes outside of this range in the provided @mm must be updated."
could be used.
Quoted 4 lines
2.
move_ptes()also operates on a source and destination ranges, but unlikecopy_pte_range()the destination range is also attached to the currently active task.
But not here.
Quoted 5 lines
3. Though theoretical, nesting sections with interleaving calls to
lazy_mmu_mode_enable()andlazy_mmu_mode_enable_for_pte_range()make it difficult to define (let alone to implement) which range is currently active, if any.
Right. I assume you would specify the source here as well, or which one would it be in your case to speed it up?
Quoted 3 lines
All of these goes away if we switch from
for_pte_range()tofast_pte_range()semantics:
I don't quite like the "fast" in there. I think you can keep the old name, but clarifying that it is merely a hint, and only ptes that fall into the hint might observe a speedup.
Could performance benefit from multiple ranges? (like in mremap, for example)?
In that case, an explicit hint interface could be reconsidered.
Signature · 3 lines
Cheers, David
Alexander Gordeev
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 10 lines · David Hildenbrand (Arm)
Quoted 5 lines
1.
copy_pte_range()operates on two ranges: source and destination. Thoughlazy_mmu_mode_enable_for_pte_range()applies to the source one, updates to the destination are still happen while in tha lazy mode. (Although the lazy mode is not actually needed for the destination unattached MM).So, here a
"No ptes outside of this range in the provided @mm must be updated."
Quoted 8 lines
could be used.
Quoted 4 lines
2.
move_ptes()also operates on a source and destination ranges, but unlikecopy_pte_range()the destination range is also attached to the currently active task.But not here.
I did not quite understand these two comments ;), but I think I address them further below.
Quoted 7 lines
Quoted 4 lines
3. Though theoretical, nesting sections with interleaving calls to
lazy_mmu_mode_enable()andlazy_mmu_mode_enable_for_pte_range()make it difficult to define (let alone to implement) which range is currently active, if any.Right. I assume you would specify the source here as well, or which one would it be in your case to speed it up?
It is in all cases the source/old/existing one.
Quoted 6 lines
Quoted 2 lines
All of these goes away if we switch from
for_pte_range()tofast_pte_range()semantics:I don't quite like the "fast" in there. I think you can keep the old name, but clarifying that it is merely a hint, and only ptes that fall into the hint might observe a speedup.
Okay, that simplify things.
Quoted 2 lines
Could performance benefit from multiple ranges? (like in mremap, for example)?
No.
Quoted 1 line
In that case, an explicit hint interface could be reconsidered.
So all things considered, how does it look?
/**
* lazy_mmu_mode_enable_for_pte_range() - Enable the lazy MMU mode with a speedup hint.
* @mm: Address space the ptes represent.
* @addr: Address of the first pte.
* @end: End address of the range.
* @ptep: Page table pointer for the first entry.
*
* Enters a new lazy MMU mode section; if the mode was not already enabled,
* enables it and calls arch_enter_lazy_mmu_mode_for_pte_range().
*
* PTEs that fall within the specified range might observe update speedups.
* The PTE range must belong to the specified memory space and do not cross
* a page table boundary.
*
* There are no requirements on the order or range completeness of PTE
* updates for the specified range.
*
* Must be paired with a call to lazy_mmu_mode_disable().
*
* Has no effect if called:
* - While paused - see lazy_mmu_mode_pause()
* - In interrupt context
*/
Quoted 4 lines
Signature · 3 lines
Cheers, David
Thanks!
David Hildenbrand (Arm)
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 14 lines · Alexander Gordeev
Quoted 13 lines · David Hildenbrand (Arm)
Quoted 5 lines
1.
copy_pte_range()operates on two ranges: source and destination. Thoughlazy_mmu_mode_enable_for_pte_range()applies to the source one, updates to the destination are still happen while in tha lazy mode. (Although the lazy mode is not actually needed for the destination unattached MM).So, here a
"No ptes outside of this range in the provided @mm must be updated."
could be used.
Quoted 1 line
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
2.
move_ptes()also operates on a source and destination ranges, but unlikecopy_pte_range()the destination range is also attached to the currently active task.But not here.
I did not quite understand these two comments ;), but I think I address them further below.
I'm saying that the second case is the problematic one ;)
Quoted 10 lines
Quoted 8 lines
Quoted 5 lines
3. Though theoretical, nesting sections with interleaving calls to
lazy_mmu_mode_enable()andlazy_mmu_mode_enable_for_pte_range()make it difficult to define (let alone to implement) which range is currently active, if any.Right. I assume you would specify the source here as well, or which one would it be in your case to speed it up?
It is in all cases the source/old/existing one.
Make sense.
Quoted 34 lines
Quoted 7 lines
Quoted 3 lines
All of these goes away if we switch from
for_pte_range()tofast_pte_range()semantics:I don't quite like the "fast" in there. I think you can keep the old name, but clarifying that it is merely a hint, and only ptes that fall into the hint might observe a speedup.
Okay, that simplify things.
Quoted 2 lines
Could performance benefit from multiple ranges? (like in mremap, for example)?
No.
Quoted 1 line
In that case, an explicit hint interface could be reconsidered.
So all things considered, how does it look?
/**
*lazy_mmu_mode_enable_for_pte_range()- Enable the lazy MMU mode with a speedup hint.
* @mm: Address space the ptes represent.
* @addr: Address of the first pte.
* @end: End address of the range.
* @ptep: Page table pointer for the first entry.
*
* Enters a new lazy MMU mode section; if the mode was not already enabled,
* enables it and callsarch_enter_lazy_mmu_mode_for_pte_range().
*
* PTEs that fall within the specified range might observe update speedups.
* The PTE range must belong to the specified memory space and do not cross
* a page table boundary.
*
* There are no requirements on the order or range completeness of PTE
Quoted 8 lines
* updates for the specified range.
*
* Must be paired with a call tolazy_mmu_mode_disable(). *
* Has no effect if called:
* - While paused - seelazy_mmu_mode_pause()
* - In interrupt context
*/
LGTM!
Signature · 3 lines
Cheers, David
Re: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
In reply to Alexander GordeevQuoted 3 lines · Alexander Gordeev
/**
*lazy_mmu_mode_enable_for_pte_range()- Enable the lazy MMU mode with a speedup hint.
* @mm: Address space the ptes represent.
Not sure that makes sense, maybe something like "Address space the PTEs belong to"?
Quoted 1 line
* @addr: Address of the first pte.
Isn't it the address of the underlying memory rather?
Quoted 8 lines
* @end: End address of the range.
* @ptep: Page table pointer for the first entry.
*
* Enters a new lazy MMU mode section; if the mode was not already enabled,
* enables it and callsarch_enter_lazy_mmu_mode_for_pte_range(). *
* PTEs that fall within the specified range might observe update speedups.
* The PTE range must belong to the specified memory space and do not cross
s/do not/not/
Quoted 11 lines
* a page table boundary.
*
* There are no requirements on the order or range completeness of PTE
* updates for the specified range.
*
* Must be paired with a call tolazy_mmu_mode_disable().
*
* Has no effect if called:
* - While paused - seelazy_mmu_mode_pause()
* - In interrupt context
*/
Looks reasonable to me otherwise.
- Kevin
Alexander Gordeev
maintainerRe: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 8 lines · Kevin Brodsky
Quoted 1 line
* @mm: Address space the ptes represent.
Not sure that makes sense, maybe something like "Address space the PTEs belong to"?
Quoted 1 line
* @addr: Address of the first pte.
Isn't it the address of the underlying memory rather?
Will update both and also the clear_not_present_full_ptes() comment, where I copied it from.
...
Quoted 4 lines
Quoted 2 lines
* PTEs that fall within the specified range might observe update speedups.
* The PTE range must belong to the specified memory space and do not crosss/do not/not/
...
Quoted 1 line
Looks reasonable to me otherwise.
Thanks!
Quoted 1 line
- Kevin
Re: [RFC PATCH 1/2] mm: make lazy MMU mode context-aware
Quoted 9 lines · Alexander Gordeev
Quoted 6 lines · Kevin Brodsky
Quoted 1 line
* @mm: Address space the ptes represent.
Not sure that makes sense, maybe something like "Address space the PTEs belong to"?
Quoted 1 line
* @addr: Address of the first pte.
Isn't it the address of the underlying memory rather?
Will update both and also the
clear_not_present_full_ptes()comment, where I copied it from.
Oh I hadn't realised those comments came from there, that would be nice, thanks. A better reference might be a function like clear_ptes(), which was probably the inspiration for clear_not_present_full_ptes() (but wording adjustments were needed for the latter since the PTEs don't point to any memory).
- Kevin
Alexander Gordeev
maintainerRFC PATCH 2/2 s390/mm: Batch PTE updates in lazy MMU mode
In reply to Alexander GordeevMake use of the IPTE instruction's "Additional Entries" field to invalidate multiple PTEs in one go while in lazy MMU mode. This is the mode in which many memory-management system calls (like mremap(), mprotect(), etc.) update memory attributes.
To achieve that, the set_pte() and ptep_get() primitives use a per-CPU cache to store and retrieve PTE values and apply the cached values to the real page table once lazy MMU mode is left.
The same is done for memory-management platform callbacks that would otherwise cause intense per-PTE IPTE traffic, reducing the number of IPTE instructions from up to PTRS_PER_PTE to a single instruction in the best case. The average reduction is of course smaller.
Since all existing page table iterators called in lazy MMU mode handle one table at a time, the per-CPU cache does not need to be larger than PTRS_PER_PTE entries. That also naturally aligns with the IPTE instruction, which must not cross a page table boundary.
Before this change, the system calls did:
lazy_mmu_mode_enable_pte() ... <update PTEs> // up to PTRS_PER_PTE single-IPTEs ... lazy_mmu_mode_disable()
With this change, the system calls do:
lazy_mmu_mode_enable_pte()
...
<store new PTE values in the per-CPU cache>
...
lazy_mmu_mode_disable() // apply cache with one multi-IPTE
When applied to large memory ranges, some system calls show significant speedups:
mprotect() ~15x
munmap() ~3x
mremap() ~28xAt the same time, fork() shows a measurable slowdown of ~1.5x.
The overall results depend on memory size and access patterns, but the change generally does not degrade performance.
In addition to a process-wide impact, the rework affects the whole Central Electronics Complex (CEC). Each (global) IPTE instruction initiates a quiesce state in a CEC, so reducing the number of IPTE calls relieves CEC-wide quiesce traffic.
In an extreme case of mprotect() contiguously triggering the quiesce state on four LPARs in parallel, measurements show ~25x fewer quiesce events.
--- arch/s390/Kconfig | 8 + arch/s390/include/asm/pgtable.h | 209 +++++++++++++++-- arch/s390/mm/Makefile | 1 + arch/s390/mm/ipte_batch.c | 396 ++++++++++++++++++++++++++++++++ arch/s390/mm/pgtable.c | 8 +- 5 files changed, 603 insertions(+), 19 deletions(-) create mode 100644 arch/s390/mm/ipte_batch.c
arch/s390/Kconfig +8 -0
@@ -732,6 +732,14 @@ config MAX_PHYSMEM_BITS Increasing the number of bits also increases the kernel image size. By default 46 bits (64TB) are supported. +config IPTE_BATCH + def_bool y + prompt "Enables Additional Entries for IPTE instruction" + select ARCH_HAS_LAZY_MMU_MODE + help + This option enables using of "Additional Entries" field of the IPTE + instruction, which capitalizes on the lazy MMU mode infrastructure. + endmenu menu "I/O subsystem"
arch/s390/include/asm/pgtable.h +194 -15
285 lines not on this page · raw message
arch/s390/mm/Makefile +1 -0
@@ -11,5 +11,6 @@ obj-$(CONFIG_DEBUG_VIRTUAL) += physaddr.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o obj-$(CONFIG_PTDUMP) += dump_pagetables.o obj-$(CONFIG_PFAULT) += pfault.o +obj-$(CONFIG_IPTE_BATCH) += ipte_batch.o obj-$(subst m,y,$(CONFIG_KVM)) += gmap_helpers.o
arch/s390/mm/ipte_batch.c new file +396 -0
396 lines not on this page · raw message
arch/s390/mm/pgtable.c +4 -4
@@ -166,14 +166,14 @@ pte_t ptep_xchg_lazy(struct mm_struct *mm, unsigned long addr, }EXPORT_SYMBOL (ptep_xchg_lazy); -pte_t ptep_modify_prot_start (struct vm_area_struct *vma,unsigned long addr, -pte_t *ptep) +pte_t ___ptep_modify_prot_start (struct vm_area_struct *vma,unsigned long addr, +pte_t *ptep) {return ptep_flush_lazy (vma->vm_mm, addr, ptep,1 ); } -void ptep_modify_prot_commit (struct vm_area_struct *vma,unsigned long addr, -pte_t *ptep,pte_t old_pte,pte_t pte) +void ___ptep_modify_prot_commit (struct vm_area_struct *vma,unsigned long addr, +pte_t *ptep,pte_t old_pte,pte_t pte) {set_pte (ptep, pte); }
Signature · 1 line
2.51.0