1 // SPDX-License-Identifier: GPL-2.0 2 #include <string.h> 3 #include <fcntl.h> 4 #include "../kselftest.h" 5 #include "vm_util.h" 6 7 #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" 8 #define SMAP_FILE_PATH "/proc/self/smaps" 9 #define MAX_LINE_LENGTH 500 10 11 uint64_t pagemap_get_entry(int fd, char *start) 12 { 13 const unsigned long pfn = (unsigned long)start / getpagesize(); 14 uint64_t entry; 15 int ret; 16 17 ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry)); 18 if (ret != sizeof(entry)) 19 ksft_exit_fail_msg("reading pagemap failed\n"); 20 return entry; 21 } 22 23 bool pagemap_is_softdirty(int fd, char *start) 24 { 25 uint64_t entry = pagemap_get_entry(fd, start); 26 27 // Check if dirty bit (55th bit) is set 28 return entry & 0x0080000000000000ull; 29 } 30 31 bool pagemap_is_swapped(int fd, char *start) 32 { 33 uint64_t entry = pagemap_get_entry(fd, start); 34 35 return entry & 0x4000000000000000ull; 36 } 37 38 bool pagemap_is_populated(int fd, char *start) 39 { 40 uint64_t entry = pagemap_get_entry(fd, start); 41 42 /* Present or swapped. */ 43 return entry & 0xc000000000000000ull; 44 } 45 46 unsigned long pagemap_get_pfn(int fd, char *start) 47 { 48 uint64_t entry = pagemap_get_entry(fd, start); 49 50 /* If present (63th bit), PFN is at bit 0 -- 54. */ 51 if (entry & 0x8000000000000000ull) 52 return entry & 0x007fffffffffffffull; 53 return -1ul; 54 } 55 56 void clear_softdirty(void) 57 { 58 int ret; 59 const char *ctrl = "4"; 60 int fd = open("/proc/self/clear_refs", O_WRONLY); 61 62 if (fd < 0) 63 ksft_exit_fail_msg("opening clear_refs failed\n"); 64 ret = write(fd, ctrl, strlen(ctrl)); 65 close(fd); 66 if (ret != strlen(ctrl)) 67 ksft_exit_fail_msg("writing clear_refs failed\n"); 68 } 69 70 bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len) 71 { 72 while (fgets(buf, len, fp)) { 73 if (!strncmp(buf, pattern, strlen(pattern))) 74 return true; 75 } 76 return false; 77 } 78 79 uint64_t read_pmd_pagesize(void) 80 { 81 int fd; 82 char buf[20]; 83 ssize_t num_read; 84 85 fd = open(PMD_SIZE_FILE_PATH, O_RDONLY); 86 if (fd == -1) 87 ksft_exit_fail_msg("Open hpage_pmd_size failed\n"); 88 89 num_read = read(fd, buf, 19); 90 if (num_read < 1) { 91 close(fd); 92 ksft_exit_fail_msg("Read hpage_pmd_size failed\n"); 93 } 94 buf[num_read] = '\0'; 95 close(fd); 96 97 return strtoul(buf, NULL, 10); 98 } 99 100 bool __check_huge(void *addr, char *pattern, int nr_hpages, 101 uint64_t hpage_size) 102 { 103 uint64_t thp = -1; 104 int ret; 105 FILE *fp; 106 char buffer[MAX_LINE_LENGTH]; 107 char addr_pattern[MAX_LINE_LENGTH]; 108 109 ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-", 110 (unsigned long) addr); 111 if (ret >= MAX_LINE_LENGTH) 112 ksft_exit_fail_msg("%s: Pattern is too long\n", __func__); 113 114 fp = fopen(SMAP_FILE_PATH, "r"); 115 if (!fp) 116 ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH); 117 118 if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer))) 119 goto err_out; 120 121 /* 122 * Fetch the pattern in the same block and check the number of 123 * hugepages. 124 */ 125 if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer))) 126 goto err_out; 127 128 snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern); 129 130 if (sscanf(buffer, addr_pattern, &thp) != 1) 131 ksft_exit_fail_msg("Reading smap error\n"); 132 133 err_out: 134 fclose(fp); 135 return thp == (nr_hpages * (hpage_size >> 10)); 136 } 137 138 bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size) 139 { 140 return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size); 141 } 142 143 bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size) 144 { 145 return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size); 146 } 147 148 bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size) 149 { 150 return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size); 151 } 152