| // SPDX-License-Identifier: GPL-2.0 |
| #include <string.h> |
| #include <fcntl.h> |
| #include "../kselftest.h" |
| #include "vm_util.h" |
| |
| #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" |
| #define SMAP_FILE_PATH "/proc/self/smaps" |
| #define MAX_LINE_LENGTH 500 |
| |
| uint64_t pagemap_get_entry(int fd, char *start) |
| { |
| const unsigned long pfn = (unsigned long)start / getpagesize(); |
| uint64_t entry; |
| int ret; |
| |
| ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry)); |
| if (ret != sizeof(entry)) |
| ksft_exit_fail_msg("reading pagemap failed\n"); |
| return entry; |
| } |
| |
| bool pagemap_is_softdirty(int fd, char *start) |
| { |
| uint64_t entry = pagemap_get_entry(fd, start); |
| |
| // Check if dirty bit (55th bit) is set |
| return entry & 0x0080000000000000ull; |
| } |
| |
| void clear_softdirty(void) |
| { |
| int ret; |
| const char *ctrl = "4"; |
| int fd = open("/proc/self/clear_refs", O_WRONLY); |
| |
| if (fd < 0) |
| ksft_exit_fail_msg("opening clear_refs failed\n"); |
| ret = write(fd, ctrl, strlen(ctrl)); |
| close(fd); |
| if (ret != strlen(ctrl)) |
| ksft_exit_fail_msg("writing clear_refs failed\n"); |
| } |
| |
| static bool check_for_pattern(FILE *fp, const char *pattern, char *buf) |
| { |
| while (fgets(buf, MAX_LINE_LENGTH, fp) != NULL) { |
| if (!strncmp(buf, pattern, strlen(pattern))) |
| return true; |
| } |
| return false; |
| } |
| |
| uint64_t read_pmd_pagesize(void) |
| { |
| int fd; |
| char buf[20]; |
| ssize_t num_read; |
| |
| fd = open(PMD_SIZE_FILE_PATH, O_RDONLY); |
| if (fd == -1) |
| ksft_exit_fail_msg("Open hpage_pmd_size failed\n"); |
| |
| num_read = read(fd, buf, 19); |
| if (num_read < 1) { |
| close(fd); |
| ksft_exit_fail_msg("Read hpage_pmd_size failed\n"); |
| } |
| buf[num_read] = '\0'; |
| close(fd); |
| |
| return strtoul(buf, NULL, 10); |
| } |
| |
| uint64_t check_huge(void *addr) |
| { |
| uint64_t thp = 0; |
| int ret; |
| FILE *fp; |
| char buffer[MAX_LINE_LENGTH]; |
| char addr_pattern[MAX_LINE_LENGTH]; |
| |
| ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-", |
| (unsigned long) addr); |
| if (ret >= MAX_LINE_LENGTH) |
| ksft_exit_fail_msg("%s: Pattern is too long\n", __func__); |
| |
| fp = fopen(SMAP_FILE_PATH, "r"); |
| if (!fp) |
| ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH); |
| |
| if (!check_for_pattern(fp, addr_pattern, buffer)) |
| goto err_out; |
| |
| /* |
| * Fetch the AnonHugePages: in the same block and check the number of |
| * hugepages. |
| */ |
| if (!check_for_pattern(fp, "AnonHugePages:", buffer)) |
| goto err_out; |
| |
| if (sscanf(buffer, "AnonHugePages:%10ld kB", &thp) != 1) |
| ksft_exit_fail_msg("Reading smap error\n"); |
| |
| err_out: |
| fclose(fp); |
| return thp; |
| } |