1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0 2baa489faSSeongJae Park #include <string.h> 3baa489faSSeongJae Park #include <fcntl.h> 4baa489faSSeongJae Park #include "../kselftest.h" 5baa489faSSeongJae Park #include "vm_util.h" 6baa489faSSeongJae Park 7baa489faSSeongJae Park #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" 8baa489faSSeongJae Park #define SMAP_FILE_PATH "/proc/self/smaps" 9baa489faSSeongJae Park #define MAX_LINE_LENGTH 500 10baa489faSSeongJae Park 11af605d26SPeter Xu unsigned int __page_size; 12af605d26SPeter Xu unsigned int __page_shift; 13af605d26SPeter Xu 14baa489faSSeongJae Park uint64_t pagemap_get_entry(int fd, char *start) 15baa489faSSeongJae Park { 16baa489faSSeongJae Park const unsigned long pfn = (unsigned long)start / getpagesize(); 17baa489faSSeongJae Park uint64_t entry; 18baa489faSSeongJae Park int ret; 19baa489faSSeongJae Park 20baa489faSSeongJae Park ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry)); 21baa489faSSeongJae Park if (ret != sizeof(entry)) 22baa489faSSeongJae Park ksft_exit_fail_msg("reading pagemap failed\n"); 23baa489faSSeongJae Park return entry; 24baa489faSSeongJae Park } 25baa489faSSeongJae Park 26baa489faSSeongJae Park bool pagemap_is_softdirty(int fd, char *start) 27baa489faSSeongJae Park { 28baa489faSSeongJae Park uint64_t entry = pagemap_get_entry(fd, start); 29baa489faSSeongJae Park 30baa489faSSeongJae Park // Check if dirty bit (55th bit) is set 31baa489faSSeongJae Park return entry & 0x0080000000000000ull; 32baa489faSSeongJae Park } 33baa489faSSeongJae Park 34baa489faSSeongJae Park bool pagemap_is_swapped(int fd, char *start) 35baa489faSSeongJae Park { 36baa489faSSeongJae Park uint64_t entry = pagemap_get_entry(fd, start); 37baa489faSSeongJae Park 38baa489faSSeongJae Park return entry & 0x4000000000000000ull; 39baa489faSSeongJae Park } 40baa489faSSeongJae Park 41baa489faSSeongJae Park bool pagemap_is_populated(int fd, char *start) 42baa489faSSeongJae Park { 43baa489faSSeongJae Park uint64_t entry = pagemap_get_entry(fd, start); 44baa489faSSeongJae Park 45baa489faSSeongJae Park /* Present or swapped. */ 46baa489faSSeongJae Park return entry & 0xc000000000000000ull; 47baa489faSSeongJae Park } 48baa489faSSeongJae Park 49baa489faSSeongJae Park unsigned long pagemap_get_pfn(int fd, char *start) 50baa489faSSeongJae Park { 51baa489faSSeongJae Park uint64_t entry = pagemap_get_entry(fd, start); 52baa489faSSeongJae Park 53baa489faSSeongJae Park /* If present (63th bit), PFN is at bit 0 -- 54. */ 54baa489faSSeongJae Park if (entry & 0x8000000000000000ull) 55baa489faSSeongJae Park return entry & 0x007fffffffffffffull; 56baa489faSSeongJae Park return -1ul; 57baa489faSSeongJae Park } 58baa489faSSeongJae Park 59baa489faSSeongJae Park void clear_softdirty(void) 60baa489faSSeongJae Park { 61baa489faSSeongJae Park int ret; 62baa489faSSeongJae Park const char *ctrl = "4"; 63baa489faSSeongJae Park int fd = open("/proc/self/clear_refs", O_WRONLY); 64baa489faSSeongJae Park 65baa489faSSeongJae Park if (fd < 0) 66baa489faSSeongJae Park ksft_exit_fail_msg("opening clear_refs failed\n"); 67baa489faSSeongJae Park ret = write(fd, ctrl, strlen(ctrl)); 68baa489faSSeongJae Park close(fd); 69baa489faSSeongJae Park if (ret != strlen(ctrl)) 70baa489faSSeongJae Park ksft_exit_fail_msg("writing clear_refs failed\n"); 71baa489faSSeongJae Park } 72baa489faSSeongJae Park 73baa489faSSeongJae Park bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len) 74baa489faSSeongJae Park { 75baa489faSSeongJae Park while (fgets(buf, len, fp)) { 76baa489faSSeongJae Park if (!strncmp(buf, pattern, strlen(pattern))) 77baa489faSSeongJae Park return true; 78baa489faSSeongJae Park } 79baa489faSSeongJae Park return false; 80baa489faSSeongJae Park } 81baa489faSSeongJae Park 82baa489faSSeongJae Park uint64_t read_pmd_pagesize(void) 83baa489faSSeongJae Park { 84baa489faSSeongJae Park int fd; 85baa489faSSeongJae Park char buf[20]; 86baa489faSSeongJae Park ssize_t num_read; 87baa489faSSeongJae Park 88baa489faSSeongJae Park fd = open(PMD_SIZE_FILE_PATH, O_RDONLY); 89baa489faSSeongJae Park if (fd == -1) 90d6e61afbSDavid Hildenbrand return 0; 91baa489faSSeongJae Park 92baa489faSSeongJae Park num_read = read(fd, buf, 19); 93baa489faSSeongJae Park if (num_read < 1) { 94baa489faSSeongJae Park close(fd); 95d6e61afbSDavid Hildenbrand return 0; 96baa489faSSeongJae Park } 97baa489faSSeongJae Park buf[num_read] = '\0'; 98baa489faSSeongJae Park close(fd); 99baa489faSSeongJae Park 100baa489faSSeongJae Park return strtoul(buf, NULL, 10); 101baa489faSSeongJae Park } 102baa489faSSeongJae Park 103baa489faSSeongJae Park bool __check_huge(void *addr, char *pattern, int nr_hpages, 104baa489faSSeongJae Park uint64_t hpage_size) 105baa489faSSeongJae Park { 106baa489faSSeongJae Park uint64_t thp = -1; 107baa489faSSeongJae Park int ret; 108baa489faSSeongJae Park FILE *fp; 109baa489faSSeongJae Park char buffer[MAX_LINE_LENGTH]; 110baa489faSSeongJae Park char addr_pattern[MAX_LINE_LENGTH]; 111baa489faSSeongJae Park 112baa489faSSeongJae Park ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-", 113baa489faSSeongJae Park (unsigned long) addr); 114baa489faSSeongJae Park if (ret >= MAX_LINE_LENGTH) 115baa489faSSeongJae Park ksft_exit_fail_msg("%s: Pattern is too long\n", __func__); 116baa489faSSeongJae Park 117baa489faSSeongJae Park fp = fopen(SMAP_FILE_PATH, "r"); 118baa489faSSeongJae Park if (!fp) 119baa489faSSeongJae Park ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH); 120baa489faSSeongJae Park 121baa489faSSeongJae Park if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer))) 122baa489faSSeongJae Park goto err_out; 123baa489faSSeongJae Park 124baa489faSSeongJae Park /* 125baa489faSSeongJae Park * Fetch the pattern in the same block and check the number of 126baa489faSSeongJae Park * hugepages. 127baa489faSSeongJae Park */ 128baa489faSSeongJae Park if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer))) 129baa489faSSeongJae Park goto err_out; 130baa489faSSeongJae Park 131baa489faSSeongJae Park snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern); 132baa489faSSeongJae Park 133baa489faSSeongJae Park if (sscanf(buffer, addr_pattern, &thp) != 1) 134baa489faSSeongJae Park ksft_exit_fail_msg("Reading smap error\n"); 135baa489faSSeongJae Park 136baa489faSSeongJae Park err_out: 137baa489faSSeongJae Park fclose(fp); 138baa489faSSeongJae Park return thp == (nr_hpages * (hpage_size >> 10)); 139baa489faSSeongJae Park } 140baa489faSSeongJae Park 141baa489faSSeongJae Park bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size) 142baa489faSSeongJae Park { 143baa489faSSeongJae Park return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size); 144baa489faSSeongJae Park } 145baa489faSSeongJae Park 146baa489faSSeongJae Park bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size) 147baa489faSSeongJae Park { 148baa489faSSeongJae Park return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size); 149baa489faSSeongJae Park } 150baa489faSSeongJae Park 151baa489faSSeongJae Park bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size) 152baa489faSSeongJae Park { 153baa489faSSeongJae Park return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size); 154baa489faSSeongJae Park } 155af605d26SPeter Xu 156af605d26SPeter Xu int64_t allocate_transhuge(void *ptr, int pagemap_fd) 157af605d26SPeter Xu { 158af605d26SPeter Xu uint64_t ent[2]; 159af605d26SPeter Xu 160af605d26SPeter Xu /* drop pmd */ 161af605d26SPeter Xu if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE, 162af605d26SPeter Xu MAP_FIXED | MAP_ANONYMOUS | 163af605d26SPeter Xu MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr) 164af605d26SPeter Xu errx(2, "mmap transhuge"); 165af605d26SPeter Xu 166af605d26SPeter Xu if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE)) 167af605d26SPeter Xu err(2, "MADV_HUGEPAGE"); 168af605d26SPeter Xu 169af605d26SPeter Xu /* allocate transparent huge page */ 170af605d26SPeter Xu *(volatile void **)ptr = ptr; 171af605d26SPeter Xu 172af605d26SPeter Xu if (pread(pagemap_fd, ent, sizeof(ent), 173af605d26SPeter Xu (uintptr_t)ptr >> (pshift() - 3)) != sizeof(ent)) 174af605d26SPeter Xu err(2, "read pagemap"); 175af605d26SPeter Xu 176af605d26SPeter Xu if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) && 177af605d26SPeter Xu PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) && 178af605d26SPeter Xu !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - pshift())) - 1))) 179af605d26SPeter Xu return PAGEMAP_PFN(ent[0]); 180af605d26SPeter Xu 181af605d26SPeter Xu return -1; 182af605d26SPeter Xu } 183*bd4d67e7SPeter Xu 184*bd4d67e7SPeter Xu unsigned long default_huge_page_size(void) 185*bd4d67e7SPeter Xu { 186*bd4d67e7SPeter Xu unsigned long hps = 0; 187*bd4d67e7SPeter Xu char *line = NULL; 188*bd4d67e7SPeter Xu size_t linelen = 0; 189*bd4d67e7SPeter Xu FILE *f = fopen("/proc/meminfo", "r"); 190*bd4d67e7SPeter Xu 191*bd4d67e7SPeter Xu if (!f) 192*bd4d67e7SPeter Xu return 0; 193*bd4d67e7SPeter Xu while (getline(&line, &linelen, f) > 0) { 194*bd4d67e7SPeter Xu if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { 195*bd4d67e7SPeter Xu hps <<= 10; 196*bd4d67e7SPeter Xu break; 197*bd4d67e7SPeter Xu } 198*bd4d67e7SPeter Xu } 199*bd4d67e7SPeter Xu 200*bd4d67e7SPeter Xu free(line); 201*bd4d67e7SPeter Xu fclose(f); 202*bd4d67e7SPeter Xu return hps; 203*bd4d67e7SPeter Xu } 204