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

selftests: vm: add a hugetlb test case

Since the head vmemmap page frame associated with each HugeTLB page is
reused, we should hide the PG_head flag of tail struct page from the
user. Add a tese case to check whether it is work properly. The test
steps are as follows.

1) alloc 2MB hugeTLB
2) get each page frame
3) apply those APIs in each page frame
4) Those APIs work completely the same as before.

Reading the flags of a page by /proc/kpageflags is done in
stable_page_flags(), which has invoked PageHead(), PageTail(),
PageCompound() and compound_head().

If those APIs work properly, the head page must have 15 and 17 bits set.
And tail pages must have 16 and 17 bits set but 15 bit unset. Those
flags are checked in check_page_flags().

Link: https://lkml.kernel.org/r/20211101031651.75851-5-songmuchun@bytedance.com
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Barry Song <song.bao.hua@hisilicon.com>
Cc: Bodeddula Balasubramaniam <bodeddub@amazon.com>
Cc: Chen Huang <chenhuang5@huawei.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Fam Zheng <fam.zheng@bytedance.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Qi Zheng <zhengqi.arch@bytedance.com>
Cc: Xiongchun Duan <duanxiongchun@bytedance.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Muchun Song and committed by
Linus Torvalds
b147c89c d8d55f56

+157
+1
tools/testing/selftests/vm/.gitignore
··· 2 2 hugepage-mmap 3 3 hugepage-mremap 4 4 hugepage-shm 5 + hugepage-vmemmap 5 6 khugepaged 6 7 map_hugetlb 7 8 map_populate
+1
tools/testing/selftests/vm/Makefile
··· 33 33 TEST_GEN_FILES += hugepage-mmap 34 34 TEST_GEN_FILES += hugepage-mremap 35 35 TEST_GEN_FILES += hugepage-shm 36 + TEST_GEN_FILES += hugepage-vmemmap 36 37 TEST_GEN_FILES += khugepaged 37 38 TEST_GEN_FILES += madv_populate 38 39 TEST_GEN_FILES += map_fixed_noreplace
+144
tools/testing/selftests/vm/hugepage-vmemmap.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * A test case of using hugepage memory in a user application using the 4 + * mmap system call with MAP_HUGETLB flag. Before running this program 5 + * make sure the administrator has allocated enough default sized huge 6 + * pages to cover the 2 MB allocation. 7 + */ 8 + #include <stdlib.h> 9 + #include <stdio.h> 10 + #include <unistd.h> 11 + #include <sys/mman.h> 12 + #include <fcntl.h> 13 + 14 + #define MAP_LENGTH (2UL * 1024 * 1024) 15 + 16 + #ifndef MAP_HUGETLB 17 + #define MAP_HUGETLB 0x40000 /* arch specific */ 18 + #endif 19 + 20 + #define PAGE_SIZE 4096 21 + 22 + #define PAGE_COMPOUND_HEAD (1UL << 15) 23 + #define PAGE_COMPOUND_TAIL (1UL << 16) 24 + #define PAGE_HUGE (1UL << 17) 25 + 26 + #define HEAD_PAGE_FLAGS (PAGE_COMPOUND_HEAD | PAGE_HUGE) 27 + #define TAIL_PAGE_FLAGS (PAGE_COMPOUND_TAIL | PAGE_HUGE) 28 + 29 + #define PM_PFRAME_BITS 55 30 + #define PM_PFRAME_MASK ~((1UL << PM_PFRAME_BITS) - 1) 31 + 32 + /* 33 + * For ia64 architecture, Linux kernel reserves Region number 4 for hugepages. 34 + * That means the addresses starting with 0x800000... will need to be 35 + * specified. Specifying a fixed address is not required on ppc64, i386 36 + * or x86_64. 37 + */ 38 + #ifdef __ia64__ 39 + #define MAP_ADDR (void *)(0x8000000000000000UL) 40 + #define MAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED) 41 + #else 42 + #define MAP_ADDR NULL 43 + #define MAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB) 44 + #endif 45 + 46 + static void write_bytes(char *addr, size_t length) 47 + { 48 + unsigned long i; 49 + 50 + for (i = 0; i < length; i++) 51 + *(addr + i) = (char)i; 52 + } 53 + 54 + static unsigned long virt_to_pfn(void *addr) 55 + { 56 + int fd; 57 + unsigned long pagemap; 58 + 59 + fd = open("/proc/self/pagemap", O_RDONLY); 60 + if (fd < 0) 61 + return -1UL; 62 + 63 + lseek(fd, (unsigned long)addr / PAGE_SIZE * sizeof(pagemap), SEEK_SET); 64 + read(fd, &pagemap, sizeof(pagemap)); 65 + close(fd); 66 + 67 + return pagemap & ~PM_PFRAME_MASK; 68 + } 69 + 70 + static int check_page_flags(unsigned long pfn) 71 + { 72 + int fd, i; 73 + unsigned long pageflags; 74 + 75 + fd = open("/proc/kpageflags", O_RDONLY); 76 + if (fd < 0) 77 + return -1; 78 + 79 + lseek(fd, pfn * sizeof(pageflags), SEEK_SET); 80 + 81 + read(fd, &pageflags, sizeof(pageflags)); 82 + if ((pageflags & HEAD_PAGE_FLAGS) != HEAD_PAGE_FLAGS) { 83 + close(fd); 84 + printf("Head page flags (%lx) is invalid\n", pageflags); 85 + return -1; 86 + } 87 + 88 + /* 89 + * pages other than the first page must be tail and shouldn't be head; 90 + * this also verifies kernel has correctly set the fake page_head to tail 91 + * while hugetlb_free_vmemmap is enabled. 92 + */ 93 + for (i = 1; i < MAP_LENGTH / PAGE_SIZE; i++) { 94 + read(fd, &pageflags, sizeof(pageflags)); 95 + if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS || 96 + (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) { 97 + close(fd); 98 + printf("Tail page flags (%lx) is invalid\n", pageflags); 99 + return -1; 100 + } 101 + } 102 + 103 + close(fd); 104 + 105 + return 0; 106 + } 107 + 108 + int main(int argc, char **argv) 109 + { 110 + void *addr; 111 + unsigned long pfn; 112 + 113 + addr = mmap(MAP_ADDR, MAP_LENGTH, PROT_READ | PROT_WRITE, MAP_FLAGS, -1, 0); 114 + if (addr == MAP_FAILED) { 115 + perror("mmap"); 116 + exit(1); 117 + } 118 + 119 + /* Trigger allocation of HugeTLB page. */ 120 + write_bytes(addr, MAP_LENGTH); 121 + 122 + pfn = virt_to_pfn(addr); 123 + if (pfn == -1UL) { 124 + munmap(addr, MAP_LENGTH); 125 + perror("virt_to_pfn"); 126 + exit(1); 127 + } 128 + 129 + printf("Returned address is %p whose pfn is %lx\n", addr, pfn); 130 + 131 + if (check_page_flags(pfn) < 0) { 132 + munmap(addr, MAP_LENGTH); 133 + perror("check_page_flags"); 134 + exit(1); 135 + } 136 + 137 + /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */ 138 + if (munmap(addr, MAP_LENGTH)) { 139 + perror("munmap"); 140 + exit(1); 141 + } 142 + 143 + return 0; 144 + }
+11
tools/testing/selftests/vm/run_vmtests.sh
··· 120 120 fi 121 121 rm -f $mnt/huge_mremap 122 122 123 + echo "------------------------" 124 + echo "running hugepage-vmemmap" 125 + echo "------------------------" 126 + ./hugepage-vmemmap 127 + if [ $? -ne 0 ]; then 128 + echo "[FAIL]" 129 + exitcode=1 130 + else 131 + echo "[PASS]" 132 + fi 133 + 123 134 echo "NOTE: The above hugetlb tests provide minimal coverage. Use" 124 135 echo " https://github.com/libhugetlbfs/libhugetlbfs.git for" 125 136 echo " hugetlb regression testing."