Skip to content

Commit c4d91e2

Browse files
lorenzo-stoakesakpm00
authored andcommitted
mm/vma: add expand-only VMA merge mode and optimise do_brk_flags()
Patch series "introduce VMA merge mode to improve brk() performance". A ~5% performance regression was discovered on the aim9.brk_test.ops_per_sec by the linux kernel test bot [0]. In the past to satisfy brk() performance we duplicated VMA expansion code and special-cased do_brk_flags(). This is however horrid and undoes work to abstract this logic, so in resolving the issue I have endeavoured to avoid this. Investigating further I was able to observe that the use of a vma_iter_next_range() and vma_prev() pair, causing an unnecessary maple tree walk. In addition there is work that we do that is simply unnecessary for brk(). Therefore, add a special VMA merge mode VMG_FLAG_JUST_EXPAND to avoid doing any of this - it assumes the VMA iterator is pointing at the previous VMA and which skips logic that brk() does not require. This mostly eliminates the performance regression reducing it to ~2% which is in the realm of noise. In addition, the will-it-scale test brk2, written to be more representative of real-world brk() usage, shows a modest performance improvement - which gives me confidence that we are not meaningfully regressing real workloads here. This series includes a test asserting that the 'just expand' mode works as expected. With many thanks to Oliver Sang for helping with performance testing of candidate patch sets! [0]:https://lore.kernel.org/linux-mm/[email protected] This patch (of 2): We know in advance that do_brk_flags() wants only to perform a VMA expansion (if the prior VMA is compatible), and that we assume no mergeable VMA follows it. These are the semantics of this function prior to the recent rewrite of the VMA merging logic, however we are now doing more work than necessary - positioning the VMA iterator at the prior VMA and performing tasks that are not required. Add a new field to the vmg struct to permit merge flags and add a new merge flag VMG_FLAG_JUST_EXPAND which implies this behaviour, and have do_brk_flags() use this. This fixes a reported performance regression in a brk() benchmarking suite. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/4e65d4395e5841c5acf8470dbcb714016364fd39.1729174352.git.lorenzo.stoakes@oracle.com Fixes: cacded5 ("mm: avoid using vma_merge() for new VMAs") Reported-by: kernel test robot <[email protected]> Closes: https://lore.kernel.org/linux-mm/[email protected] Signed-off-by: Lorenzo Stoakes <[email protected]> Reviewed-by: Liam R. Howlett <[email protected]> Cc: Jann Horn <[email protected]> Cc: Vlastimil Babka <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent b125a0d commit c4d91e2

File tree

3 files changed

+31
-9
lines changed

3 files changed

+31
-9
lines changed

mm/mmap.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1756,7 +1756,8 @@ static int do_brk_flags(struct vma_iterator *vmi, struct vm_area_struct *vma,
17561756
VMG_STATE(vmg, mm, vmi, addr, addr + len, flags, PHYS_PFN(addr));
17571757

17581758
vmg.prev = vma;
1759-
vma_iter_next_range(vmi);
1759+
/* vmi is positioned at prev, which this mode expects. */
1760+
vmg.merge_flags = VMG_FLAG_JUST_EXPAND;
17601761

17611762
if (vma_merge_new_range(&vmg))
17621763
goto out;

mm/vma.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,7 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg)
917917
pgoff_t pgoff = vmg->pgoff;
918918
pgoff_t pglen = PHYS_PFN(end - start);
919919
bool can_merge_left, can_merge_right;
920+
bool just_expand = vmg->merge_flags & VMG_FLAG_JUST_EXPAND;
920921

921922
mmap_assert_write_locked(vmg->mm);
922923
VM_WARN_ON(vmg->vma);
@@ -930,7 +931,7 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg)
930931
return NULL;
931932

932933
can_merge_left = can_vma_merge_left(vmg);
933-
can_merge_right = can_vma_merge_right(vmg, can_merge_left);
934+
can_merge_right = !just_expand && can_vma_merge_right(vmg, can_merge_left);
934935

935936
/* If we can merge with the next VMA, adjust vmg accordingly. */
936937
if (can_merge_right) {
@@ -953,7 +954,11 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg)
953954
if (can_merge_right && !can_merge_remove_vma(next))
954955
vmg->end = end;
955956

956-
vma_prev(vmg->vmi); /* Equivalent to going to the previous range */
957+
/* In expand-only case we are already positioned at prev. */
958+
if (!just_expand) {
959+
/* Equivalent to going to the previous range. */
960+
vma_prev(vmg->vmi);
961+
}
957962
}
958963

959964
/*
@@ -967,12 +972,14 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg)
967972
}
968973

969974
/* If expansion failed, reset state. Allows us to retry merge later. */
970-
vmg->vma = NULL;
971-
vmg->start = start;
972-
vmg->end = end;
973-
vmg->pgoff = pgoff;
974-
if (vmg->vma == prev)
975-
vma_iter_set(vmg->vmi, start);
975+
if (!just_expand) {
976+
vmg->vma = NULL;
977+
vmg->start = start;
978+
vmg->end = end;
979+
vmg->pgoff = pgoff;
980+
if (vmg->vma == prev)
981+
vma_iter_set(vmg->vmi, start);
982+
}
976983

977984
return NULL;
978985
}

mm/vma.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ enum vma_merge_state {
5959
VMA_MERGE_SUCCESS,
6060
};
6161

62+
enum vma_merge_flags {
63+
VMG_FLAG_DEFAULT = 0,
64+
/*
65+
* If we can expand, simply do so. We know there is nothing to merge to
66+
* the right. Does not reset state upon failure to merge. The VMA
67+
* iterator is assumed to be positioned at the previous VMA, rather than
68+
* at the gap.
69+
*/
70+
VMG_FLAG_JUST_EXPAND = 1 << 0,
71+
};
72+
6273
/* Represents a VMA merge operation. */
6374
struct vma_merge_struct {
6475
struct mm_struct *mm;
@@ -75,6 +86,7 @@ struct vma_merge_struct {
7586
struct mempolicy *policy;
7687
struct vm_userfaultfd_ctx uffd_ctx;
7788
struct anon_vma_name *anon_name;
89+
enum vma_merge_flags merge_flags;
7890
enum vma_merge_state state;
7991
};
8092

@@ -99,6 +111,7 @@ static inline pgoff_t vma_pgoff_offset(struct vm_area_struct *vma,
99111
.flags = flags_, \
100112
.pgoff = pgoff_, \
101113
.state = VMA_MERGE_START, \
114+
.merge_flags = VMG_FLAG_DEFAULT, \
102115
}
103116

104117
#define VMG_VMA_STATE(name, vmi_, prev_, vma_, start_, end_) \
@@ -118,6 +131,7 @@ static inline pgoff_t vma_pgoff_offset(struct vm_area_struct *vma,
118131
.uffd_ctx = vma_->vm_userfaultfd_ctx, \
119132
.anon_name = anon_vma_name(vma_), \
120133
.state = VMA_MERGE_START, \
134+
.merge_flags = VMG_FLAG_DEFAULT, \
121135
}
122136

123137
#ifdef CONFIG_DEBUG_VM_MAPLE_TREE

0 commit comments

Comments
 (0)