0001
0002
0003
0004
0005
0006
0007 #define _GNU_SOURCE
0008 #include <stdio.h>
0009 #include <stdlib.h>
0010 #include <stdarg.h>
0011 #include <unistd.h>
0012 #include <inttypes.h>
0013 #include <string.h>
0014 #include <fcntl.h>
0015 #include <sys/mman.h>
0016 #include <sys/mount.h>
0017 #include <malloc.h>
0018 #include <stdbool.h>
0019 #include "vm_util.h"
0020
0021 uint64_t pagesize;
0022 unsigned int pageshift;
0023 uint64_t pmd_pagesize;
0024
0025 #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
0026 #define INPUT_MAX 80
0027
0028 #define PID_FMT "%d,0x%lx,0x%lx"
0029 #define PATH_FMT "%s,0x%lx,0x%lx"
0030
0031 #define PFN_MASK ((1UL<<55)-1)
0032 #define KPF_THP (1UL<<22)
0033
0034 int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file)
0035 {
0036 uint64_t paddr;
0037 uint64_t page_flags;
0038
0039 if (pagemap_file) {
0040 pread(pagemap_file, &paddr, sizeof(paddr),
0041 ((long)vaddr >> pageshift) * sizeof(paddr));
0042
0043 if (kpageflags_file) {
0044 pread(kpageflags_file, &page_flags, sizeof(page_flags),
0045 (paddr & PFN_MASK) * sizeof(page_flags));
0046
0047 return !!(page_flags & KPF_THP);
0048 }
0049 }
0050 return 0;
0051 }
0052
0053 static int write_file(const char *path, const char *buf, size_t buflen)
0054 {
0055 int fd;
0056 ssize_t numwritten;
0057
0058 fd = open(path, O_WRONLY);
0059 if (fd == -1)
0060 return 0;
0061
0062 numwritten = write(fd, buf, buflen - 1);
0063 close(fd);
0064 if (numwritten < 1)
0065 return 0;
0066
0067 return (unsigned int) numwritten;
0068 }
0069
0070 static void write_debugfs(const char *fmt, ...)
0071 {
0072 char input[INPUT_MAX];
0073 int ret;
0074 va_list argp;
0075
0076 va_start(argp, fmt);
0077 ret = vsnprintf(input, INPUT_MAX, fmt, argp);
0078 va_end(argp);
0079
0080 if (ret >= INPUT_MAX) {
0081 printf("%s: Debugfs input is too long\n", __func__);
0082 exit(EXIT_FAILURE);
0083 }
0084
0085 if (!write_file(SPLIT_DEBUGFS, input, ret + 1)) {
0086 perror(SPLIT_DEBUGFS);
0087 exit(EXIT_FAILURE);
0088 }
0089 }
0090
0091 void split_pmd_thp(void)
0092 {
0093 char *one_page;
0094 size_t len = 4 * pmd_pagesize;
0095 uint64_t thp_size;
0096 size_t i;
0097
0098 one_page = memalign(pmd_pagesize, len);
0099
0100 if (!one_page) {
0101 printf("Fail to allocate memory\n");
0102 exit(EXIT_FAILURE);
0103 }
0104
0105 madvise(one_page, len, MADV_HUGEPAGE);
0106
0107 for (i = 0; i < len; i++)
0108 one_page[i] = (char)i;
0109
0110 thp_size = check_huge(one_page);
0111 if (!thp_size) {
0112 printf("No THP is allocated\n");
0113 exit(EXIT_FAILURE);
0114 }
0115
0116
0117 write_debugfs(PID_FMT, getpid(), (uint64_t)one_page,
0118 (uint64_t)one_page + len);
0119
0120 for (i = 0; i < len; i++)
0121 if (one_page[i] != (char)i) {
0122 printf("%ld byte corrupted\n", i);
0123 exit(EXIT_FAILURE);
0124 }
0125
0126
0127 thp_size = check_huge(one_page);
0128 if (thp_size) {
0129 printf("Still %ld kB AnonHugePages not split\n", thp_size);
0130 exit(EXIT_FAILURE);
0131 }
0132
0133 printf("Split huge pages successful\n");
0134 free(one_page);
0135 }
0136
0137 void split_pte_mapped_thp(void)
0138 {
0139 char *one_page, *pte_mapped, *pte_mapped2;
0140 size_t len = 4 * pmd_pagesize;
0141 uint64_t thp_size;
0142 size_t i;
0143 const char *pagemap_template = "/proc/%d/pagemap";
0144 const char *kpageflags_proc = "/proc/kpageflags";
0145 char pagemap_proc[255];
0146 int pagemap_fd;
0147 int kpageflags_fd;
0148
0149 if (snprintf(pagemap_proc, 255, pagemap_template, getpid()) < 0) {
0150 perror("get pagemap proc error");
0151 exit(EXIT_FAILURE);
0152 }
0153 pagemap_fd = open(pagemap_proc, O_RDONLY);
0154
0155 if (pagemap_fd == -1) {
0156 perror("read pagemap:");
0157 exit(EXIT_FAILURE);
0158 }
0159
0160 kpageflags_fd = open(kpageflags_proc, O_RDONLY);
0161
0162 if (kpageflags_fd == -1) {
0163 perror("read kpageflags:");
0164 exit(EXIT_FAILURE);
0165 }
0166
0167 one_page = mmap((void *)(1UL << 30), len, PROT_READ | PROT_WRITE,
0168 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
0169
0170 madvise(one_page, len, MADV_HUGEPAGE);
0171
0172 for (i = 0; i < len; i++)
0173 one_page[i] = (char)i;
0174
0175 thp_size = check_huge(one_page);
0176 if (!thp_size) {
0177 printf("No THP is allocated\n");
0178 exit(EXIT_FAILURE);
0179 }
0180
0181
0182 pte_mapped = mremap(one_page, pagesize, pagesize, MREMAP_MAYMOVE);
0183
0184
0185 for (i = 1; i < 4; i++) {
0186 pte_mapped2 = mremap(one_page + pmd_pagesize * i + pagesize * i,
0187 pagesize, pagesize,
0188 MREMAP_MAYMOVE|MREMAP_FIXED,
0189 pte_mapped + pagesize * i);
0190 if (pte_mapped2 == (char *)-1) {
0191 perror("mremap failed");
0192 exit(EXIT_FAILURE);
0193 }
0194 }
0195
0196
0197 thp_size = 0;
0198 for (i = 0; i < pagesize * 4; i++)
0199 if (i % pagesize == 0 &&
0200 is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
0201 thp_size++;
0202
0203 if (thp_size != 4) {
0204 printf("Some THPs are missing during mremap\n");
0205 exit(EXIT_FAILURE);
0206 }
0207
0208
0209 write_debugfs(PID_FMT, getpid(), (uint64_t)pte_mapped,
0210 (uint64_t)pte_mapped + pagesize * 4);
0211
0212
0213 thp_size = 0;
0214 for (i = 0; i < pagesize * 4; i++) {
0215 if (pte_mapped[i] != (char)i) {
0216 printf("%ld byte corrupted\n", i);
0217 exit(EXIT_FAILURE);
0218 }
0219 if (i % pagesize == 0 &&
0220 is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
0221 thp_size++;
0222 }
0223
0224 if (thp_size) {
0225 printf("Still %ld THPs not split\n", thp_size);
0226 exit(EXIT_FAILURE);
0227 }
0228
0229 printf("Split PTE-mapped huge pages successful\n");
0230 munmap(one_page, len);
0231 close(pagemap_fd);
0232 close(kpageflags_fd);
0233 }
0234
0235 void split_file_backed_thp(void)
0236 {
0237 int status;
0238 int fd;
0239 ssize_t num_written;
0240 char tmpfs_template[] = "/tmp/thp_split_XXXXXX";
0241 const char *tmpfs_loc = mkdtemp(tmpfs_template);
0242 char testfile[INPUT_MAX];
0243 uint64_t pgoff_start = 0, pgoff_end = 1024;
0244
0245 printf("Please enable pr_debug in split_huge_pages_in_file() if you need more info.\n");
0246
0247 status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m");
0248
0249 if (status) {
0250 printf("Unable to create a tmpfs for testing\n");
0251 exit(EXIT_FAILURE);
0252 }
0253
0254 status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc);
0255 if (status >= INPUT_MAX) {
0256 printf("Fail to create file-backed THP split testing file\n");
0257 goto cleanup;
0258 }
0259
0260 fd = open(testfile, O_CREAT|O_WRONLY);
0261 if (fd == -1) {
0262 perror("Cannot open testing file\n");
0263 goto cleanup;
0264 }
0265
0266
0267 num_written = write(fd, tmpfs_loc, strlen(tmpfs_loc) + 1);
0268 close(fd);
0269
0270 if (num_written < 1) {
0271 printf("Fail to write data to testing file\n");
0272 goto cleanup;
0273 }
0274
0275
0276 write_debugfs(PATH_FMT, testfile, pgoff_start, pgoff_end);
0277
0278 status = unlink(testfile);
0279 if (status)
0280 perror("Cannot remove testing file\n");
0281
0282 cleanup:
0283 status = umount(tmpfs_loc);
0284 if (status) {
0285 printf("Unable to umount %s\n", tmpfs_loc);
0286 exit(EXIT_FAILURE);
0287 }
0288 status = rmdir(tmpfs_loc);
0289 if (status) {
0290 perror("cannot remove tmp dir");
0291 exit(EXIT_FAILURE);
0292 }
0293
0294 printf("file-backed THP split test done, please check dmesg for more information\n");
0295 }
0296
0297 int main(int argc, char **argv)
0298 {
0299 if (geteuid() != 0) {
0300 printf("Please run the benchmark as root\n");
0301 exit(EXIT_FAILURE);
0302 }
0303
0304 pagesize = getpagesize();
0305 pageshift = ffs(pagesize) - 1;
0306 pmd_pagesize = read_pmd_pagesize();
0307
0308 split_pmd_thp();
0309 split_pte_mapped_thp();
0310 split_file_backed_thp();
0311
0312 return 0;
0313 }