I write a testcase[1] to demonstrate this scenario. vanilla kernel (v6.6): # ./test [mmap rw ] vaddr: 0x0x7f2000000000, pfn: 0x110000, is_thp: 1, is_thp_mapped: 1 [mprotect ro] vaddr: 0x0x7f2000000000, pfn: 0x110000, is_thp: 1, is_thp_mapped: 0 ^^ __split_huge_pmd() is called in change_protection() [mprotect rw] vaddr: 0x0x7f2000000000, pfn: 0x110000, is_thp: 1, is_thp_mapped: 0 ^^ vma_merge() [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x110000, is_thp: 1, is_thp_mapped: 0 [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x110000, is_thp: 1, is_thp_mapped: 0 [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x117a00, is_thp: 1, is_thp_mapped: 1 ^^ hpage_collapse_scan_pmd ^^^^^^^^ new hugepage is allocated [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x117a00, is_thp: 1, is_thp_mapped: 1 patched kernel: # ./test [mmap rw ] vaddr: 0x0x7f2000000000, pfn: 0x128400, is_thp: 1, is_thp_mapped: 1 [mprotect ro] vaddr: 0x0x7f2000000000, pfn: 0x128400, is_thp: 1, is_thp_mapped: 0 [mprotect rw] vaddr: 0x0x7f2000000000, pfn: 0x128400, is_thp: 1, is_thp_mapped: 0 [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x128400, is_thp: 1, is_thp_mapped: 0 [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x128400, is_thp: 1, is_thp_mapped: 0 [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x128400, is_thp: 1, is_thp_mapped: 1 ^^ hpage_collapse_scan_pmd ^^^^^^^^ old hugepage [khugepaged ] vaddr: 0x0x7f2000000000, pfn: 0x128400, is_thp: 1, is_thp_mapped: 1 [1] testcase source code #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <ctype.h> #include <fcntl.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <unistd.h> #include<signal.h> #define PAGE_SHIFT 12 #define SIZE_2M (2<<20) #define ADDRESS 0x7f2000000000 #define PAGEMAP_PRESENT 63 #define PAGEMAP_PFN_MASK 0x007FFFFFFFFFFFFFUL /* bits 54:0 */ #define KPF_SIZE 8 #define KPF_THP 22 static bool is_thp(unsigned long pfn) { int kpageflags_fd; unsigned long flag; int ret; kpageflags_fd = open("/proc/kpageflags", O_RDONLY); ret = pread(kpageflags_fd, &flag, KPF_SIZE, KPF_SIZE * pfn); close(kpageflags_fd); if (ret != KPF_SIZE) return false; return flag & (1 << KPF_THP); } static bool is_thp_mapped(void *addr) { char path[64], line[128], pattern[64]; int pid; FILE *fp; int anon_huge_pages = 0; snprintf(pattern, 64, "%lx", (unsigned long)addr); pid = getpid(); snprintf(path, 64, "/proc/%u/smaps", pid); fp = fopen(path, "r"); while (fgets(line, 64, fp) != NULL) { if (strstr(line, pattern)) break; } while (fgets(line, 64, fp) != NULL) { if (1 == sscanf(line, "AnonHugePages: %d kB", &anon_huge_pages)) break; } fclose(fp); return anon_huge_pages != 0; } static unsigned long get_pfn(unsigned long vaddr) { char path[64]; int pid, pagemap_fd; off64_t offset; unsigned long pfn = 0; int ret; offset = (vaddr >> PAGE_SHIFT) * sizeof(unsigned long); pid = getpid(); snprintf(path, 64, "/proc/%u/pagemap", pid); pagemap_fd = open(path, O_RDONLY); lseek64(pagemap_fd, offset, SEEK_SET); ret = read(pagemap_fd, &pfn, sizeof(pfn)); close(pagemap_fd); if (ret < 0) return -1; if (!(pfn & (1UL << PAGEMAP_PRESENT))) return 0; return (pfn & PAGEMAP_PFN_MASK); } static void show_addr_info(void *addr, char *msg) { unsigned long pfn; pfn = get_pfn((unsigned long)addr); printf("[%s] vaddr: 0x%p, pfn: 0x%lx, is_thp: %d, is_thp_mapped: %d\n", msg, addr, pfn, is_thp(pfn), is_thp_mapped(addr)); } int main(int argc, char *argv[]) { void *addr; addr = mmap((void *)ADDRESS, SIZE_2M, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0); if (addr == MAP_FAILED) { perror("mmap"); return 1; } show_addr_info(addr, "mmap rw "); mprotect(addr, SIZE_2M / 2, PROT_READ); show_addr_info(addr, "mprotect ro"); mprotect(addr, SIZE_2M / 2, PROT_READ | PROT_WRITE); show_addr_info(addr, "mprotect rw"); while (1) { show_addr_info(addr, "khugepaged "); sleep(5); } return 0; } Xu Yu (1): mm/khugepaged: map anonymous pte-mapped THPs by pmds mm/khugepaged.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 180 insertions(+), 7 deletions(-) -- 2.37.1