Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: add merging after mremap resize

When mremap call results in expansion, it might be possible to merge the
VMA with the next VMA which might become adjacent. This patch adds
vma_merge call after the expansion is done to try and merge.

[akpm@linux-foundation.org: coding-style cleanups]
Link: https://lkml.kernel.org/r/20220603145719.1012094-3-matenajakub@gmail.com
Signed-off-by: Jakub Matěna <matenajakub@gmail.com>
Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
Cc: Hugh Dickins <hughd@google.com>
Cc: "Kirill A . Shutemov" <kirill@shutemov.name>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Rik van Riel <riel@surriel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Jakub Matěna and committed by
Andrew Morton
ca3d76b0 eef19944

+65 -3
+17 -2
mm/mremap.c
··· 9 9 */ 10 10 11 11 #include <linux/mm.h> 12 + #include <linux/mm_inline.h> 12 13 #include <linux/hugetlb.h> 13 14 #include <linux/shm.h> 14 15 #include <linux/ksm.h> ··· 24 23 #include <linux/mmu_notifier.h> 25 24 #include <linux/uaccess.h> 26 25 #include <linux/userfaultfd_k.h> 26 + #include <linux/mempolicy.h> 27 27 28 28 #include <asm/cacheflush.h> 29 29 #include <asm/tlb.h> ··· 1014 1012 /* can we just expand the current mapping? */ 1015 1013 if (vma_expandable(vma, new_len - old_len)) { 1016 1014 long pages = (new_len - old_len) >> PAGE_SHIFT; 1015 + unsigned long extension_start = addr + old_len; 1016 + unsigned long extension_end = addr + new_len; 1017 + pgoff_t extension_pgoff = vma->vm_pgoff + (old_len >> PAGE_SHIFT); 1017 1018 1018 1019 if (vma->vm_flags & VM_ACCOUNT) { 1019 1020 if (security_vm_enough_memory_mm(mm, pages)) { ··· 1025 1020 } 1026 1021 } 1027 1022 1028 - if (vma_adjust(vma, vma->vm_start, addr + new_len, 1029 - vma->vm_pgoff, NULL)) { 1023 + /* 1024 + * Function vma_merge() is called on the extension we are adding to 1025 + * the already existing vma, vma_merge() will merge this extension with 1026 + * the already existing vma (expand operation itself) and possibly also 1027 + * with the next vma if it becomes adjacent to the expanded vma and 1028 + * otherwise compatible. 1029 + */ 1030 + vma = vma_merge(mm, vma, extension_start, extension_end, 1031 + vma->vm_flags, vma->anon_vma, vma->vm_file, 1032 + extension_pgoff, vma_policy(vma), 1033 + vma->vm_userfaultfd_ctx, anon_vma_name(vma)); 1034 + if (!vma) { 1030 1035 vm_unacct_memory(pages); 1031 1036 ret = -ENOMEM; 1032 1037 goto out;
+48 -1
tools/testing/selftests/vm/mremap_test.c
··· 119 119 } 120 120 121 121 /* 122 + * This test validates that merge is called when expanding a mapping. 123 + * Mapping containing three pages is created, middle page is unmapped 124 + * and then the mapping containing the first page is expanded so that 125 + * it fills the created hole. The two parts should merge creating 126 + * single mapping with three pages. 127 + */ 128 + static void mremap_expand_merge(unsigned long page_size) 129 + { 130 + char *test_name = "mremap expand merge"; 131 + FILE *fp; 132 + char *line = NULL; 133 + size_t len = 0; 134 + bool success = false; 135 + char *start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, 136 + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 137 + 138 + munmap(start + page_size, page_size); 139 + mremap(start, page_size, 2 * page_size, 0); 140 + 141 + fp = fopen("/proc/self/maps", "r"); 142 + if (fp == NULL) { 143 + ksft_test_result_fail("%s\n", test_name); 144 + return; 145 + } 146 + 147 + while (getline(&line, &len, fp) != -1) { 148 + char *first = strtok(line, "- "); 149 + void *first_val = (void *)strtol(first, NULL, 16); 150 + char *second = strtok(NULL, "- "); 151 + void *second_val = (void *) strtol(second, NULL, 16); 152 + 153 + if (first_val == start && second_val == start + 3 * page_size) { 154 + success = true; 155 + break; 156 + } 157 + } 158 + if (success) 159 + ksft_test_result_pass("%s\n", test_name); 160 + else 161 + ksft_test_result_fail("%s\n", test_name); 162 + fclose(fp); 163 + } 164 + 165 + /* 122 166 * Returns the start address of the mapping on success, else returns 123 167 * NULL on failure. 124 168 */ ··· 380 336 int i, run_perf_tests; 381 337 unsigned int threshold_mb = VALIDATION_DEFAULT_THRESHOLD; 382 338 unsigned int pattern_seed; 339 + int num_expand_tests = 1; 383 340 struct test test_cases[MAX_TEST]; 384 341 struct test perf_test_cases[MAX_PERF_TEST]; 385 342 int page_size; ··· 452 407 (threshold_mb * _1MB >= _1GB); 453 408 454 409 ksft_set_plan(ARRAY_SIZE(test_cases) + (run_perf_tests ? 455 - ARRAY_SIZE(perf_test_cases) : 0)); 410 + ARRAY_SIZE(perf_test_cases) : 0) + num_expand_tests); 456 411 457 412 for (i = 0; i < ARRAY_SIZE(test_cases); i++) 458 413 run_mremap_test_case(test_cases[i], &failures, threshold_mb, 459 414 pattern_seed); 415 + 416 + mremap_expand_merge(page_size); 460 417 461 418 if (run_perf_tests) { 462 419 ksft_print_msg("\n%s\n",