1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * hugetlb-mmap: 4 * 5 * Example of using huge page memory in a user application using the mmap 6 * system call. Before running this application, make sure that the 7 * administrator has mounted the hugetlbfs filesystem (on some directory 8 * like /mnt) using the command mount -t hugetlbfs nodev /mnt. In this 9 * example, the app is requesting memory of size 256MB that is backed by 10 * huge pages. 11 */ 12 #define _GNU_SOURCE 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <unistd.h> 16 #include <sys/mman.h> 17 #include <fcntl.h> 18 #include <linux/memfd.h> 19 #include "vm_util.h" 20 #include "kselftest.h" 21 #include "hugepage_settings.h" 22 23 #define LENGTH (256UL*1024*1024) 24 #define PROTECTION (PROT_READ | PROT_WRITE) 25 26 static void check_bytes(char *addr) 27 { 28 ksft_print_msg("First hex is %x\n", *((unsigned int *)addr)); 29 } 30 31 static void write_bytes(char *addr, size_t length) 32 { 33 unsigned long i; 34 35 for (i = 0; i < length; i++) 36 *(addr + i) = (char)i; 37 } 38 39 static bool verify_bytes(char *addr, size_t length) 40 { 41 unsigned long i; 42 43 check_bytes(addr); 44 for (i = 0; i < length; i++) 45 if (*(addr + i) != (char)i) { 46 ksft_print_msg("Error: Mismatch at %lu(%p)\n", i, addr); 47 return false; 48 } 49 50 return true; 51 } 52 53 static void test_mmap(size_t length, int mmap_flags, int fd, 54 const char *test_name) 55 { 56 bool passed = true; 57 void *addr; 58 59 addr = mmap(NULL, length, PROTECTION, mmap_flags, fd, 0); 60 if (addr == MAP_FAILED) 61 ksft_exit_fail_perror("mmap"); 62 63 ksft_print_msg("Returned address is %p\n", addr); 64 check_bytes(addr); 65 write_bytes(addr, length); 66 if (!verify_bytes(addr, length)) 67 passed = false; 68 69 /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */ 70 if (munmap(addr, length)) 71 ksft_exit_fail_perror("munmap"); 72 73 ksft_test_result(passed, "%s\n", test_name); 74 } 75 76 static void test_anon_mmap(size_t length, int shift) 77 { 78 const char *test_name = "hugetlb anonymous mmap"; 79 int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; 80 81 if (shift) 82 mmap_flags |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; 83 84 test_mmap(length, mmap_flags, -1, test_name); 85 } 86 87 static void test_file_mmap(size_t length, int shift) 88 { 89 const char *test_name = "hugetlb file mmap"; 90 int mfd_flags = MFD_HUGETLB; 91 int fd; 92 93 if (shift) 94 mfd_flags |= (shift & MFD_HUGE_MASK) << MFD_HUGE_SHIFT; 95 96 fd = memfd_create("hugetlb-mmap", mfd_flags); 97 if (fd < 0) 98 ksft_exit_fail_perror("memfd_create"); 99 100 test_mmap(length, MAP_SHARED, fd, test_name); 101 close(fd); 102 } 103 104 int main(int argc, char **argv) 105 { 106 size_t hugepage_size; 107 size_t length = LENGTH; 108 int shift = 0, nr; 109 110 ksft_print_header(); 111 112 if (argc > 1) 113 length = atol(argv[1]) << 20; 114 if (argc > 2) 115 shift = atoi(argv[2]); 116 117 hugetlb_save_settings(); 118 if (shift) { 119 hugepage_size = (1UL << shift); 120 ksft_print_msg("%lu kB hugepages\n", 1UL << (shift - 10)); 121 } else { 122 hugepage_size = default_huge_page_size(); 123 if (!hugepage_size) 124 ksft_exit_skip("Could not detect default hugetlb page size."); 125 ksft_print_msg("Default size hugepages (%lu kB)\n", hugepage_size >> 10); 126 } 127 128 /* munmap will fail if the length is not page aligned */ 129 length = (length + hugepage_size - 1) & ~(hugepage_size - 1); 130 nr = length / hugepage_size; 131 132 hugetlb_set_nr_pages(hugepage_size, nr); 133 if (hugetlb_free_pages(hugepage_size) < nr) 134 ksft_exit_skip("Not enough %lu Kb pages\n", hugepage_size >> 10); 135 136 ksft_set_plan(2); 137 ksft_print_msg("Mapping %lu Mbytes\n", (unsigned long)length >> 20); 138 139 test_anon_mmap(length, shift); 140 test_file_mmap(length, shift); 141 142 ksft_finished(); 143 } 144